- dissolve core-bridge into api (wire contract), domain (models), services (what the UI calls) and worker (everything that runs in the Web Worker, grouped by account, crypto, space, files, shares, runtime); regroup features, lib and store by responsibility and delete unused components - the UI only reads backend capabilities (domain/capabilities); one tokenRole picks the token, hasCode/isRetryable handle errors; pre-checks that decided permissions on the client are gone; tests fail if those rules are reimplemented - files go straight to the file domain (create, upload, download, delete, commit, cancel); the host sees one registration per file; creating a channel allocates a file domain and works even when the file service is down - file panel shows which channel the files belong to and its space usage; unlimited files preview in chat; videos get a stored cover; the file service status is shown - fix: console flooded by Retry-After reads on every upload frame
12 lines
640 B
TypeScript
12 lines
640 B
TypeScript
import styles from './PaneTabs.module.css';
|
|
|
|
export type SideTab = 'files' | 'members';
|
|
|
|
/** 桌面右栏的「文件 / 成员」切换 */
|
|
export function PaneTabs({ tab, memberCount, onTab }: { tab: SideTab; memberCount?: number; onTab: (tab: SideTab) => void }) {
|
|
return <div className={styles.tabs} role="tablist" aria-label="右侧栏">
|
|
<button type="button" role="tab" aria-selected={tab === 'files'} onClick={() => onTab('files')}>文件</button>
|
|
<button type="button" role="tab" aria-selected={tab === 'members'} onClick={() => onTab('members')}>成员{memberCount ? <span>{memberCount}</span> : null}</button>
|
|
</div>;
|
|
}
|