@deepseek-ai/dsh-client-ui-sidebar-right 0.1.5-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +133 -0
- package/README.zh.md +133 -0
- package/lib/client.js +3614 -0
- package/lib/index.js +6 -0
- package/lib/types/client/contract/params.d.ts +47 -0
- package/lib/types/client/contract/seed.d.ts +35 -0
- package/lib/types/client/contract/slots.d.ts +152 -0
- package/lib/types/client/index.d.ts +56 -0
- package/lib/types/client/labels.d.ts +19 -0
- package/lib/types/client/locales.d.ts +48 -0
- package/lib/types/client/service.d.ts +300 -0
- package/lib/types/client/shell/ExpandButton.d.ts +24 -0
- package/lib/types/client/shell/SidebarRight.d.ts +99 -0
- package/lib/types/client/stores.d.ts +100 -0
- package/lib/types/client/tab-domain.d.ts +95 -0
- package/lib/types/client/tab-info.d.ts +29 -0
- package/lib/types/client/tab-registry.d.ts +205 -0
- package/lib/types/client/tabs/guide/GuideBody.d.ts +30 -0
- package/lib/types/client/tabs/guide/definition.d.ts +18 -0
- package/lib/types/index.d.ts +4 -0
- package/package.json +79 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The way into a hidden panel: one button in the conversation header's corner
|
|
3
|
+
* seat, shown only while the panel is collapsed.
|
|
4
|
+
*
|
|
5
|
+
* It lives in the conversation's own header rather than in the frame's right
|
|
6
|
+
* column so that a collapsed Sidebar costs the conversation nothing — no rail,
|
|
7
|
+
* no width, and the transcript's scrollbar stays at the column's edge. The
|
|
8
|
+
* corner seat is its own, past the utilities' edge, so the button neither joins
|
|
9
|
+
* the utilities row nor moves it: while the panel is shown this renders a
|
|
10
|
+
* same-size placeholder, and the seat's width stays reserved. It shares the
|
|
11
|
+
* panel's per-session store, which the slot runtime allows because both seats
|
|
12
|
+
* are session-scoped.
|
|
13
|
+
*
|
|
14
|
+
* The glyph is the left sidebar's collapse icon mirrored: the same affordance,
|
|
15
|
+
* on the other edge.
|
|
16
|
+
*/
|
|
17
|
+
import type { ReactNode } from 'react';
|
|
18
|
+
import type { PropsLocale, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
|
|
19
|
+
import type { createSidebarRightStore } from '../stores.ts';
|
|
20
|
+
/** The button's props: the header corner seat, the shared store, and copy. */
|
|
21
|
+
export type ExpandButtonProps = PropsRuntime<'conversation.session.header.corner'> & PropsStore<ReturnType<typeof createSidebarRightStore>> & PropsLocale<'sidebarRight'>;
|
|
22
|
+
/** The expand control while the panel is collapsed; its footprint while it is shown. */
|
|
23
|
+
export declare function ExpandButton({ sessionId, useStore, actions, t }: ExpandButtonProps): ReactNode;
|
|
24
|
+
//# sourceMappingURL=ExpandButton.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { HostObservable, InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
+
import type { DockIntents, TabRecord } from '@deepseek-ai/dsh-client-ui-dockkit';
|
|
4
|
+
import type { HalvesFit, PaneId } from '@deepseek-ai/dsh-client-ui-dockkit';
|
|
5
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
6
|
+
import type { SidebarRightOpenTabOptions } from '../service.ts';
|
|
7
|
+
import type { SidebarRightTabDefinition } from '../tab-registry.ts';
|
|
8
|
+
import type { createSidebarRightStore, SurfaceState } from '../stores.ts';
|
|
9
|
+
import type { TabOccurrence } from '../tab-domain.ts';
|
|
10
|
+
import type { SidebarRightTabNavigation } from '../contract/slots.ts';
|
|
11
|
+
/** The store share the seat receives. */
|
|
12
|
+
type Store = PropsStore<ReturnType<typeof createSidebarRightStore>>;
|
|
13
|
+
/** The child seats this component renders. */
|
|
14
|
+
type Children = PropsRenderSlots<'sidebar.right.pane.tab' | 'sidebar.right.pane.tab.title' | 'sidebar.right.tab.menu.item'>;
|
|
15
|
+
/** What the panel reports to the frame: drawn or not, and whether it wants a track. */
|
|
16
|
+
export interface SidebarRightPresentation {
|
|
17
|
+
/** Whether the panel is drawn at all. */
|
|
18
|
+
readonly shown: boolean;
|
|
19
|
+
/** Whether the drawn panel wants the conversation to make room for it. */
|
|
20
|
+
readonly track: boolean;
|
|
21
|
+
/** Whether the panel fills the viewport, independently of its retained track. */
|
|
22
|
+
readonly fullscreen: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** What this package needs from its host beyond the framework shares. */
|
|
25
|
+
export interface SidebarRightInjected {
|
|
26
|
+
/**
|
|
27
|
+
* Report the panel's presentation to the frame.
|
|
28
|
+
*
|
|
29
|
+
* The frame sizes the track and places the resize handle; this only tells it
|
|
30
|
+
* the composition of the facts this package owns, and is called whenever that
|
|
31
|
+
* composition changes.
|
|
32
|
+
*/
|
|
33
|
+
readonly syncPresentation: (presentation: SidebarRightPresentation) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Publish this seat's session, actions, and the store's surfaces to `ctx.sidebarRight`.
|
|
36
|
+
*
|
|
37
|
+
* The service is root-scoped and cannot read a per-entry store, so the only
|
|
38
|
+
* honest source is the mounted seat. Held for as long as the seat is mounted.
|
|
39
|
+
* @param binding - what a command needs to act on this session, and what a tab's own action needs to act on its.
|
|
40
|
+
* @returns a release callback.
|
|
41
|
+
*/
|
|
42
|
+
readonly bindService: (binding: {
|
|
43
|
+
sessionId: SessionId;
|
|
44
|
+
actions: Store['actions'];
|
|
45
|
+
/** Every session's surface as last committed; the mounted one is `surfaces[sessionId]`. */
|
|
46
|
+
surfaces: Readonly<Record<string, SurfaceState>>;
|
|
47
|
+
/** The room rule's verdict for a docked pane, as the kit last measured it. */
|
|
48
|
+
canSplitPane: (paneId: PaneId) => boolean;
|
|
49
|
+
}) => () => void;
|
|
50
|
+
/**
|
|
51
|
+
* The navigation face's `openTab`, for the strip's add control: a new tab is
|
|
52
|
+
* the guide opened by kind, through the same path as every other open.
|
|
53
|
+
*/
|
|
54
|
+
readonly openTab: (kind: string, options?: SidebarRightOpenTabOptions) => void;
|
|
55
|
+
readonly hooks: {
|
|
56
|
+
readonly tabTypes: HostObservable<readonly SidebarRightTabDefinition[]>;
|
|
57
|
+
};
|
|
58
|
+
readonly keyedHooks: {
|
|
59
|
+
readonly tabNavigation: (key: string) => HostObservable<SidebarRightTabNavigation>;
|
|
60
|
+
};
|
|
61
|
+
/** Read a committed record's lifetime; never creates an occurrence. */
|
|
62
|
+
readonly occurrence: (tab: Pick<TabRecord, 'id'>) => TabOccurrence;
|
|
63
|
+
}
|
|
64
|
+
/** The column seat's props: session scope, so the session arrives as a standard prop. */
|
|
65
|
+
export type RightbarSeatProps = PropsRuntime<'rightbar'> & Children & Store & PropsLocale<'sidebarRight'> & InjectFace<SidebarRightInjected>;
|
|
66
|
+
/** Everything the panel needs, already bound to one session. */
|
|
67
|
+
interface PanelProps {
|
|
68
|
+
readonly sessionId: SessionId;
|
|
69
|
+
readonly surface: SurfaceState;
|
|
70
|
+
readonly actions: Store['actions'];
|
|
71
|
+
readonly t: RightbarSeatProps['t'];
|
|
72
|
+
readonly renderSlot: Children['renderSlot'];
|
|
73
|
+
readonly openTab: SidebarRightInjected['openTab'];
|
|
74
|
+
readonly useTabTypes: RightbarSeatProps['useTabTypes'];
|
|
75
|
+
readonly useTabNavigation: RightbarSeatProps['useTabNavigation'];
|
|
76
|
+
readonly useStore: Store['useStore'];
|
|
77
|
+
readonly occurrence: SidebarRightInjected['occurrence'];
|
|
78
|
+
readonly fullscreen: boolean;
|
|
79
|
+
readonly autoFullscreen: boolean;
|
|
80
|
+
/** Receives the kit's room-rule readings for the service's `split`. */
|
|
81
|
+
readonly reportRoom: (fits: ReadonlyMap<PaneId, HalvesFit>) => void;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Build the kit's intent face for one session out of the store's actions.
|
|
85
|
+
* @param sessionId - the session the seat draws; every action is bound to it.
|
|
86
|
+
* @param actions - the seat's bound store actions.
|
|
87
|
+
* @param openTab - the navigation face's `openTab`, which the strip's add control asks for a guide through.
|
|
88
|
+
* @returns the intents the kit reports gestures to.
|
|
89
|
+
*/
|
|
90
|
+
export declare function intentsFor(sessionId: SessionId, actions: Store['actions'], openTab: PanelProps['openTab']): DockIntents;
|
|
91
|
+
/**
|
|
92
|
+
* The right column's occupant: the panel, anchored to the column's edge and
|
|
93
|
+
* shown or hidden by sliding, plus the floating layer. It is also where the
|
|
94
|
+
* frame learns the panel's presentation, and where `ctx.sidebarRight` learns
|
|
95
|
+
* which session it is acting on, because this is the seat that knows both.
|
|
96
|
+
*/
|
|
97
|
+
export declare function RightbarSeat({ sessionId, width, viewportWidth, canShow, useStore, actions, t, renderSlot, syncPresentation, bindService, openTab, useTabTypes, useTabNavigation, occurrence, }: RightbarSeatProps): ReactNode;
|
|
98
|
+
export {};
|
|
99
|
+
//# sourceMappingURL=SidebarRight.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store shell over the docking kit: one surface per session, held as plain
|
|
3
|
+
* data so the kit's pure functions are the only thing that ever computes a
|
|
4
|
+
* layout.
|
|
5
|
+
*
|
|
6
|
+
* Every action follows the same steps — mint the ids the intent needs, ask the
|
|
7
|
+
* kit's planner what operations carry it out, let the settle planner keep every
|
|
8
|
+
* pane populated, record it all as one history entry — and then assigns the
|
|
9
|
+
* session's whole surface back in one go. Nothing here reaches into a draft to
|
|
10
|
+
* edit a layout in place, which is what keeps the kit testable without a store
|
|
11
|
+
* and keeps snapshot identity honest.
|
|
12
|
+
*
|
|
13
|
+
* The settle step is this product's rule, not the kit's: a docked pane never
|
|
14
|
+
* stays empty, and the last pane reseeds the guide tab, so there is always at
|
|
15
|
+
* least one tab to look at.
|
|
16
|
+
*
|
|
17
|
+
* A focus that changes nothing — a tab already active in its already-active
|
|
18
|
+
* pane, a pane already active — plans nothing and records nothing, whoever
|
|
19
|
+
* asks: the kit's chip click and `ctx.sidebarRight.focus` alike.
|
|
20
|
+
*
|
|
21
|
+
* So is the guide's uniqueness: a pane holds at most one guide tab. Opening the
|
|
22
|
+
* guide into a pane that has one focuses it, and a guide dragged, dropped, or
|
|
23
|
+
* docked into such a pane merges into it — the arriving guide closes and the
|
|
24
|
+
* pane's own is focused. The kit plans none of this; it is decided here before
|
|
25
|
+
* its planners run.
|
|
26
|
+
*/
|
|
27
|
+
import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-store';
|
|
28
|
+
import type { DockMode, DockZone, FloatRect, History, LayoutState, PaneId, SplitId, TabId } from '@deepseek-ai/dsh-client-ui-dockkit';
|
|
29
|
+
/** One session's docking surface: the layout, its sequence, and the id counter. */
|
|
30
|
+
export interface SurfaceState {
|
|
31
|
+
readonly layout: LayoutState;
|
|
32
|
+
readonly history: History;
|
|
33
|
+
/** How many ids this surface has minted; carried so replay stays reproducible. */
|
|
34
|
+
readonly minted: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Every session's surface, keyed by session id.
|
|
38
|
+
*
|
|
39
|
+
* Written mutable because an action receives this type as its draft; the
|
|
40
|
+
* immutability that matters is behavioural — actions only ever assign a whole
|
|
41
|
+
* new map, never reach into one.
|
|
42
|
+
*/
|
|
43
|
+
export interface SidebarRightState {
|
|
44
|
+
bySession: Record<string, SurfaceState>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* What the navigation controller asks the store to open, the address already
|
|
48
|
+
* claimed. Placement is by `replaceTab` first, then `paneId`, then the active pane.
|
|
49
|
+
*/
|
|
50
|
+
export interface OpenContentIntent {
|
|
51
|
+
readonly kind: string;
|
|
52
|
+
readonly contentId: string;
|
|
53
|
+
readonly title: string;
|
|
54
|
+
/** Land a new tab in this pane. */
|
|
55
|
+
readonly paneId?: PaneId;
|
|
56
|
+
/** Take this tab's pane and slot, and close it in the same entry. */
|
|
57
|
+
readonly replaceTab?: TabId;
|
|
58
|
+
/** `false` opens another tab even when the identity is already shown; defaults to `true`. */
|
|
59
|
+
readonly revealIfOpened?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The surface a session starts with: collapsed, one pane, one guide tab.
|
|
63
|
+
* @param seedTitle - the guide type's display name at mint time.
|
|
64
|
+
* @returns the initial surface.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createSurface(seedTitle: () => string): SurfaceState;
|
|
67
|
+
/** Declared write set; each entry is one settled intent. */
|
|
68
|
+
type SidebarRightActions = {
|
|
69
|
+
open: (draft: SidebarRightState, sessionId: string) => void;
|
|
70
|
+
setExpanded: (draft: SidebarRightState, sessionId: string, expanded: boolean) => void;
|
|
71
|
+
toggleExpanded: (draft: SidebarRightState, sessionId: string) => void;
|
|
72
|
+
setMode: (draft: SidebarRightState, sessionId: string, mode: DockMode) => void;
|
|
73
|
+
splitPane: (draft: SidebarRightState, sessionId: string, paneId?: PaneId, settled?: (paneId: PaneId) => void) => void;
|
|
74
|
+
openContent: (draft: SidebarRightState, sessionId: string, intent: OpenContentIntent, settled: (tabId: TabId) => void) => void;
|
|
75
|
+
duplicateTab: (draft: SidebarRightState, sessionId: string, tabId: TabId) => void;
|
|
76
|
+
closeTab: (draft: SidebarRightState, sessionId: string, tabId: TabId) => void;
|
|
77
|
+
focusTab: (draft: SidebarRightState, sessionId: string, tabId: TabId) => void;
|
|
78
|
+
focusPane: (draft: SidebarRightState, sessionId: string, paneId: PaneId) => void;
|
|
79
|
+
placeTab: (draft: SidebarRightState, sessionId: string, tabId: TabId, toPaneId: PaneId, index: number) => void;
|
|
80
|
+
dropTab: (draft: SidebarRightState, sessionId: string, tabId: TabId, paneId: PaneId, zone: DockZone) => void;
|
|
81
|
+
floatTab: (draft: SidebarRightState, sessionId: string, tabId: TabId, rect?: FloatRect) => void;
|
|
82
|
+
unfloatPane: (draft: SidebarRightState, sessionId: string, paneId: PaneId) => void;
|
|
83
|
+
moveFloat: (draft: SidebarRightState, sessionId: string, paneId: PaneId, x: number, y: number) => void;
|
|
84
|
+
resizeFloat: (draft: SidebarRightState, sessionId: string, paneId: PaneId, rect: FloatRect) => void;
|
|
85
|
+
resizeSplit: (draft: SidebarRightState, sessionId: string, splitId: SplitId, sizes: readonly number[]) => void;
|
|
86
|
+
undo: (draft: SidebarRightState, sessionId: string) => void;
|
|
87
|
+
redo: (draft: SidebarRightState, sessionId: string) => void;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Create the Sidebar store handle.
|
|
91
|
+
*
|
|
92
|
+
* The seed title arrives as a thunk rather than a string: a pane is seeded
|
|
93
|
+
* whenever one is created, which can be long after the store was built and in a
|
|
94
|
+
* language the user has since changed to.
|
|
95
|
+
* @param seedTitle - the guide type's display name, read at each mint.
|
|
96
|
+
* @returns the handle (spec, type, identity, and factory in one).
|
|
97
|
+
*/
|
|
98
|
+
export declare function createSidebarRightStore(seedTitle: () => string): EngineStoreHandle<SidebarRightState, SidebarRightActions>;
|
|
99
|
+
export {};
|
|
100
|
+
//# sourceMappingURL=stores.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Tab domain: what a tab record carries that the layout does not.
|
|
3
|
+
*
|
|
4
|
+
* One `TabOccurrence` per open record, keyed by (session, tab id): where the
|
|
5
|
+
* tab was navigated to, an abort signal spanning the record's life, and the
|
|
6
|
+
* actions the tab may take on itself. Holding a record also pins its address in
|
|
7
|
+
* the resource model, so switching tabs unmounts a body without dropping its
|
|
8
|
+
* content.
|
|
9
|
+
*
|
|
10
|
+
* `sync` reconciles occurrences against one session's layout after every
|
|
11
|
+
* commit: a record that appeared is pinned, one that vanished (closed, or its
|
|
12
|
+
* open undone) is aborted and dropped. A record restored by undo is a new
|
|
13
|
+
* occurrence and is fetched again if the resource model already let it go. The
|
|
14
|
+
* controller syncs each session from that session's adopted store on every
|
|
15
|
+
* commit, on screen or not, so a record closed from another session's seat is
|
|
16
|
+
* aborted on that commit. The slot framework binds the navigation sources for
|
|
17
|
+
* each record's `useTabInfo` reader.
|
|
18
|
+
*/
|
|
19
|
+
import type { LayoutState, TabId, TabRecord } from '@deepseek-ai/dsh-client-ui-dockkit';
|
|
20
|
+
import { type SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
21
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
22
|
+
import type { SidebarRightNavigationParams } from './contract/params.ts';
|
|
23
|
+
import type { SidebarRightTabActions, SidebarRightTabNavigation } from './contract/slots.ts';
|
|
24
|
+
import type { SidebarRightOpenResourceOptions, SidebarRightOpenTabOptions } from './service.ts';
|
|
25
|
+
/**
|
|
26
|
+
* The navigation face a tab's actions call back into, aimed at the session the
|
|
27
|
+
* tab is in; nothing happens for a session whose store is not adopted.
|
|
28
|
+
*/
|
|
29
|
+
export interface SidebarRightNavigator {
|
|
30
|
+
/** Open a resource in one session; see `ISidebarRight.openResource`. */
|
|
31
|
+
openResourceIn(sessionId: SessionId, address: string, options?: SidebarRightOpenResourceOptions): void;
|
|
32
|
+
/** Open a page type in one session; see `ISidebarRight.openTab`. */
|
|
33
|
+
openTabIn(sessionId: SessionId, kind: string, options?: SidebarRightOpenTabOptions): void;
|
|
34
|
+
/** Close a tab of one session. */
|
|
35
|
+
closeIn(sessionId: SessionId, tabId: TabId): void;
|
|
36
|
+
}
|
|
37
|
+
/** `ctx.resources.pin`: hold an address's content open for as long as `signal` lives. */
|
|
38
|
+
export type PinResource = (address: string, signal: AbortSignal) => void;
|
|
39
|
+
/** What one open tab record holds beyond its layout entry. */
|
|
40
|
+
export interface TabOccurrence {
|
|
41
|
+
readonly sessionId: SessionId;
|
|
42
|
+
readonly tabId: TabId;
|
|
43
|
+
/** Aborted when the record disappears or this package unloads. */
|
|
44
|
+
readonly signal: AbortSignal;
|
|
45
|
+
/** The latest navigation aimed at the record; `set` on every `navigate`. */
|
|
46
|
+
readonly navigation: SnapshotStore<SidebarRightTabNavigation>;
|
|
47
|
+
/** Stable for the occurrence's life, so a body may hold it. */
|
|
48
|
+
readonly tabActions: SidebarRightTabActions;
|
|
49
|
+
}
|
|
50
|
+
/** Every session's occurrences. */
|
|
51
|
+
export declare class TabDomain {
|
|
52
|
+
private readonly navigator;
|
|
53
|
+
private readonly pin;
|
|
54
|
+
private readonly bySession;
|
|
55
|
+
/**
|
|
56
|
+
* @param navigator - where tab actions go, aimed at the tab's session; the navigation controller.
|
|
57
|
+
* @param pin - `ctx.resources.pin`, called once per occurrence at its first sync.
|
|
58
|
+
*/
|
|
59
|
+
constructor(navigator: SidebarRightNavigator, pin: PinResource);
|
|
60
|
+
/**
|
|
61
|
+
* Reconcile one session's occurrences with its committed layout.
|
|
62
|
+
*
|
|
63
|
+
* Called by the seat after every commit, and only then: aborting a vanished
|
|
64
|
+
* record runs the types' cleanup, which writes their stores.
|
|
65
|
+
* @param sessionId - the session whose layout committed.
|
|
66
|
+
* @param layout - that session's layout as committed.
|
|
67
|
+
*/
|
|
68
|
+
sync(sessionId: SessionId, layout: LayoutState): void;
|
|
69
|
+
/**
|
|
70
|
+
* Read an occurrence created by navigation or committed-store reconciliation.
|
|
71
|
+
* @param sessionId - the session the record is in.
|
|
72
|
+
* @param tab - the record being drawn.
|
|
73
|
+
* @returns its occurrence.
|
|
74
|
+
* @throws when the record has not been reconciled or has disappeared.
|
|
75
|
+
*/
|
|
76
|
+
occurrence(sessionId: SessionId, tab: Pick<TabRecord, 'id'>): TabOccurrence;
|
|
77
|
+
/**
|
|
78
|
+
* Record that an `open` settled on a tab.
|
|
79
|
+
*
|
|
80
|
+
* A record the layout has not yet shown the seat gets its occurrence here, so
|
|
81
|
+
* the body's first render already carries the opener's `params`.
|
|
82
|
+
* @param sessionId - the session opened into.
|
|
83
|
+
* @param tabId - the tab the open settled on.
|
|
84
|
+
* @param target - the address and the opener's params.
|
|
85
|
+
*/
|
|
86
|
+
navigate(sessionId: SessionId, tabId: TabId, target: {
|
|
87
|
+
address: string;
|
|
88
|
+
params: SidebarRightNavigationParams;
|
|
89
|
+
}): void;
|
|
90
|
+
/** Abort every occurrence of every session; the package is unloading. */
|
|
91
|
+
dispose(): void;
|
|
92
|
+
private session;
|
|
93
|
+
private hold;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=tab-domain.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { TabId } from '@deepseek-ai/dsh-client-ui-dockkit';
|
|
2
|
+
import type { KeyedSnapshotSelectorHook, PropsStore, SlotHookFactory } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
+
import type { SidebarRightTabActions, SidebarRightTabNavigation, UseSidebarRightTabInfo } from './contract/slots.ts';
|
|
4
|
+
import type { createSidebarRightStore } from './stores.ts';
|
|
5
|
+
/** Stable dispatch identity and framework hooks; never passed as tab component props. */
|
|
6
|
+
export interface TabHookContext {
|
|
7
|
+
readonly tabId: TabId;
|
|
8
|
+
readonly title: boolean;
|
|
9
|
+
readonly fullscreen: boolean;
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
readonly actions: SidebarRightTabActions;
|
|
12
|
+
readonly useStore: PropsStore<ReturnType<typeof createSidebarRightStore>>['useStore'];
|
|
13
|
+
readonly useTabNavigation: KeyedSnapshotSelectorHook<SidebarRightTabNavigation>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Bind a tab occurrence without subscribing or creating records during factory evaluation.
|
|
17
|
+
* @param standard - framework session identity.
|
|
18
|
+
* @param context - stable record lifetime and framework-bound readers.
|
|
19
|
+
* @returns the tab information hook.
|
|
20
|
+
*/
|
|
21
|
+
export declare const tabInfoFactory: SlotHookFactory<'sidebar.right.pane.tab', UseSidebarRightTabInfo>;
|
|
22
|
+
/**
|
|
23
|
+
* Forward the framework-bound tab hook to a guide replacement.
|
|
24
|
+
* @param _standard - the guide's framework standard props.
|
|
25
|
+
* @param useTabInfo - the enclosing tab's framework-bound reader.
|
|
26
|
+
* @returns the same reader for the replacement.
|
|
27
|
+
*/
|
|
28
|
+
export declare const guideTabInfoFactory: SlotHookFactory<'sidebar.right.tab.guide', UseSidebarRightTabInfo>;
|
|
29
|
+
//# sourceMappingURL=tab-info.d.ts.map
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage one of tab-type registration: what a type IS.
|
|
3
|
+
*
|
|
4
|
+
* A registration is purely static — which addresses the type recognizes, how it
|
|
5
|
+
* ranks against other types that recognize the same one, what the tab chip says,
|
|
6
|
+
* and whether the type offers an entry box on the guide page. Nothing here is
|
|
7
|
+
* per-tab, per-session, or a runtime hook: stage two is the keyed
|
|
8
|
+
* `sidebar.right.pane.tab` registration that supplies the body under the same
|
|
9
|
+
* `kind`, and everything a body needs at runtime arrives in its props.
|
|
10
|
+
*
|
|
11
|
+
* Address recognition follows VS Code's editor resolver: a glob declaration
|
|
12
|
+
* narrows the candidates, an optional `canOpen` predicate vetoes, and the
|
|
13
|
+
* survivors are ranked by priority band, then by matched-pattern length, then by
|
|
14
|
+
* registration order. Addresses are `scheme://` URIs; the one local change to
|
|
15
|
+
* VS Code's glob rule is that a pattern containing `:` matches the whole address
|
|
16
|
+
* (`dsh-resource://file/**`, `sidebar://guide`) rather than the URI's path.
|
|
17
|
+
*
|
|
18
|
+
* A kind may carry one `builtin` and one `extension` registration at once: the
|
|
19
|
+
* extension is the one in force — claims, `get`, the guide page, and the body
|
|
20
|
+
* and title, which the seat finds under the definition's own `id` — and the
|
|
21
|
+
* builtin resumes when the extension unregisters. Everything else colliding on
|
|
22
|
+
* a kind throws, as does a second registration of an `id`.
|
|
23
|
+
*
|
|
24
|
+
* Thunked copy (`title`, `guide[].title`) is read again on every use, so a
|
|
25
|
+
* language change needs no re-registration.
|
|
26
|
+
*/
|
|
27
|
+
import type { ComponentType } from 'react';
|
|
28
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
29
|
+
import type { IconProps } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
30
|
+
/**
|
|
31
|
+
* How strongly a type wants an address it recognizes, as one of three literal
|
|
32
|
+
* bands (a string, not an imported constant, so a type shipped from another
|
|
33
|
+
* package needs no runtime import from here).
|
|
34
|
+
*
|
|
35
|
+
* - `extension` — a type from outside the product, and the highest: a type that
|
|
36
|
+
* declares nothing outranks every viewer shipped here, exactly as in VS Code.
|
|
37
|
+
* It is also the band that may take over a `builtin` kind.
|
|
38
|
+
* - `builtin` — the ordinary band for types shipped with the product.
|
|
39
|
+
* - `fallback` — plain-content viewers that anything more specific should beat.
|
|
40
|
+
* VS Code's text editor holds this position implicitly; ours is a separate
|
|
41
|
+
* package, so it says so.
|
|
42
|
+
*/
|
|
43
|
+
export type SidebarRightTabPriority = 'extension' | 'builtin' | 'fallback';
|
|
44
|
+
/** One entry box the guide page offers, contributed by the type it opens (picking it opens that type as a page). */
|
|
45
|
+
export interface SidebarRightGuideEntry {
|
|
46
|
+
/** Ascending position among every registered type's entries. */
|
|
47
|
+
readonly order: number;
|
|
48
|
+
/**
|
|
49
|
+
* The box's heading.
|
|
50
|
+
* @returns the heading in the current language.
|
|
51
|
+
*/
|
|
52
|
+
readonly title: () => string;
|
|
53
|
+
/**
|
|
54
|
+
* One line under the heading.
|
|
55
|
+
* @returns the line in the current language.
|
|
56
|
+
*/
|
|
57
|
+
readonly description: () => string;
|
|
58
|
+
/** Optional glyph, drawn at the box's leading edge. */
|
|
59
|
+
readonly icon?: ComponentType<IconProps>;
|
|
60
|
+
}
|
|
61
|
+
/** A guide entry as the registry lists it: with the kind of the type that contributed it, which is what picking it opens. */
|
|
62
|
+
export interface SidebarRightGuideBox extends SidebarRightGuideEntry {
|
|
63
|
+
readonly kind: string;
|
|
64
|
+
}
|
|
65
|
+
/** One registered tab type: its static face, and nothing else. */
|
|
66
|
+
export interface SidebarRightTabDefinition {
|
|
67
|
+
/**
|
|
68
|
+
* This implementation's identity in the tab system, unique across every
|
|
69
|
+
* registration (a package name is the natural value). A kind is not unique —
|
|
70
|
+
* an extension may take a builtin's over — so the implementation carries its
|
|
71
|
+
* own name, and it is the key its body and title register under in the
|
|
72
|
+
* `sidebar.right.pane.tab` and `sidebar.right.pane.tab.title` seats.
|
|
73
|
+
*/
|
|
74
|
+
readonly id: string;
|
|
75
|
+
/** Type discriminator: what the tabs of this type are, and what `openTab` names. */
|
|
76
|
+
readonly kind: string;
|
|
77
|
+
/**
|
|
78
|
+
* Resource-address globs this type recognizes; omit for a page type, which is
|
|
79
|
+
* opened by kind and recognizes no address.
|
|
80
|
+
*
|
|
81
|
+
* A pattern containing `:` is matched against the whole address
|
|
82
|
+
* (`dsh-resource://file/**`); one without is matched against the URI's path at
|
|
83
|
+
* any depth (`*.md` matches `dsh-resource://file/session/s1/home/me/notes.md`),
|
|
84
|
+
* and an address that is not a URI matches no such pattern. Matching ignores
|
|
85
|
+
* case and does not hide dotfiles.
|
|
86
|
+
*/
|
|
87
|
+
readonly patterns?: readonly string[];
|
|
88
|
+
/** Defaults to `extension`: a type that says nothing is one from outside the product. */
|
|
89
|
+
readonly priority?: SidebarRightTabPriority;
|
|
90
|
+
/**
|
|
91
|
+
* Veto an address this type's globs matched.
|
|
92
|
+
*
|
|
93
|
+
* Synchronous and cheap: it runs on every routing decision. Omit it to accept
|
|
94
|
+
* every match.
|
|
95
|
+
* @param address - the matched address.
|
|
96
|
+
* @returns whether this type will open it.
|
|
97
|
+
*/
|
|
98
|
+
readonly canOpen?: (address: string) => boolean;
|
|
99
|
+
/**
|
|
100
|
+
* The tab chip's initial text, captured into the layout record at open time.
|
|
101
|
+
* @param address - the address being opened.
|
|
102
|
+
* @returns the title in the current language.
|
|
103
|
+
*/
|
|
104
|
+
readonly title: (address: string) => string;
|
|
105
|
+
/** Entry boxes for the guide page. Omit to stay off it. */
|
|
106
|
+
readonly guide?: readonly SidebarRightGuideEntry[];
|
|
107
|
+
}
|
|
108
|
+
/** What a routing decision settles on: who draws the address, and as what. */
|
|
109
|
+
export interface SidebarRightTabClaim {
|
|
110
|
+
/** The claiming type. */
|
|
111
|
+
readonly kind: string;
|
|
112
|
+
/**
|
|
113
|
+
* Stable identity of the content, which is the address itself.
|
|
114
|
+
*
|
|
115
|
+
* Two opens of the same address are the same tab, which is what makes opening
|
|
116
|
+
* idempotent.
|
|
117
|
+
*/
|
|
118
|
+
readonly contentId: string;
|
|
119
|
+
/** Title for the tab chip. */
|
|
120
|
+
readonly title: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The registered tab types.
|
|
124
|
+
*
|
|
125
|
+
* Registration order is part of the contract: it breaks ties between types that
|
|
126
|
+
* recognize an address equally well.
|
|
127
|
+
*/
|
|
128
|
+
export declare class SidebarRightTabRegistry {
|
|
129
|
+
private readonly ctx;
|
|
130
|
+
private readonly kinds;
|
|
131
|
+
private readonly ids;
|
|
132
|
+
private readonly listeners;
|
|
133
|
+
private registrations;
|
|
134
|
+
private cached;
|
|
135
|
+
private guideEntries;
|
|
136
|
+
/** @param ctx - Context whose effects own the contributed types. */
|
|
137
|
+
constructor(ctx: Context);
|
|
138
|
+
/**
|
|
139
|
+
* Register one tab type for the caller's lifetime.
|
|
140
|
+
*
|
|
141
|
+
* The caller holds the returned disposer inside its own `ctx.effect`, so a
|
|
142
|
+
* type's registration lives exactly as long as the plugin that contributed it.
|
|
143
|
+
* An `extension` may register a kind a `builtin` already holds and takes it
|
|
144
|
+
* over until it unregisters; a second registration in the same band, or any
|
|
145
|
+
* registration meeting a `fallback` of the same kind, is a wiring mistake, and
|
|
146
|
+
* so is an `id` already in use.
|
|
147
|
+
* @param definition - the contributed type.
|
|
148
|
+
* @returns idempotent disposer.
|
|
149
|
+
* @throws when the id is taken, or the kind is already registered in a way this one cannot coexist with.
|
|
150
|
+
*/
|
|
151
|
+
register(definition: SidebarRightTabDefinition): () => void;
|
|
152
|
+
/** Add a registration to its kind's slot, the higher band in force; `coexists` has already admitted it. */
|
|
153
|
+
private enter;
|
|
154
|
+
/** Remove a registration from its kind's slot: a shadowed builtin resumes, and an emptied kind is freed. */
|
|
155
|
+
private leave;
|
|
156
|
+
/** Every kind's registration in force, in registration order. */
|
|
157
|
+
private active;
|
|
158
|
+
/**
|
|
159
|
+
* Registered types in registration order.
|
|
160
|
+
* @returns reference-stable entries.
|
|
161
|
+
*/
|
|
162
|
+
entries(): readonly SidebarRightTabDefinition[];
|
|
163
|
+
/**
|
|
164
|
+
* Every type in force's guide entries, in `order`, each naming the kind it opens.
|
|
165
|
+
* @returns reference-stable entries.
|
|
166
|
+
*/
|
|
167
|
+
guide(): readonly SidebarRightGuideBox[];
|
|
168
|
+
/**
|
|
169
|
+
* The type in force for a kind.
|
|
170
|
+
* @param kind - the type discriminator.
|
|
171
|
+
* @returns the type, or `undefined` when nothing registered it.
|
|
172
|
+
*/
|
|
173
|
+
get(kind: string): SidebarRightTabDefinition | undefined;
|
|
174
|
+
/**
|
|
175
|
+
* Every type that would open an address, best first.
|
|
176
|
+
*
|
|
177
|
+
* Ranked by priority band, then by the length of the pattern that matched,
|
|
178
|
+
* then by registration order. Types whose `canOpen` vetoes are absent.
|
|
179
|
+
* @param address - the address a caller wants opened.
|
|
180
|
+
* @returns the ranked types; empty when nothing recognizes the address.
|
|
181
|
+
*/
|
|
182
|
+
candidates(address: string): readonly SidebarRightTabDefinition[];
|
|
183
|
+
/**
|
|
184
|
+
* Decide which type opens an address, and as what.
|
|
185
|
+
*
|
|
186
|
+
* Without `kind`, the best candidate wins. With `kind`, that type opens the
|
|
187
|
+
* address if its `canOpen` agrees — its globs are not consulted, because
|
|
188
|
+
* naming the type IS the decision.
|
|
189
|
+
*
|
|
190
|
+
* An address no type will open is a wiring mistake, not a user error, so this
|
|
191
|
+
* throws rather than reporting absence.
|
|
192
|
+
* @param address - the address a caller wants opened.
|
|
193
|
+
* @param kind - a type named by the caller, overriding the ranking.
|
|
194
|
+
* @returns the claiming type and the record to open.
|
|
195
|
+
*/
|
|
196
|
+
claim(address: string, kind?: string): SidebarRightTabClaim;
|
|
197
|
+
/**
|
|
198
|
+
* Observe low-frequency registry changes.
|
|
199
|
+
* @param listener - synchronous invalidation callback.
|
|
200
|
+
* @returns unsubscribe callback.
|
|
201
|
+
*/
|
|
202
|
+
subscribe(listener: () => void): () => void;
|
|
203
|
+
private refresh;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=tab-registry.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guide tab's body: a chain host, and the guide it falls back to.
|
|
3
|
+
*
|
|
4
|
+
* The chain is the replacement seam. A product with its own idea of what an
|
|
5
|
+
* empty sidebar should say registers into `sidebar.right.tab.guide`, and its entry
|
|
6
|
+
* takes the whole body; with no entry, or with every entry declining, the guide
|
|
7
|
+
* below renders. The shipped guide is the owner's fallback rather than a chain
|
|
8
|
+
* entry of its own, so there is always exactly one body and the shipped one
|
|
9
|
+
* cannot be outvoted by accident.
|
|
10
|
+
*
|
|
11
|
+
* The shipped guide is a centred title, one line under it, and the entry boxes
|
|
12
|
+
* every registered type contributed. Picking a box opens that type as a page in
|
|
13
|
+
* this tab's place, so the guide is a doorway rather than a page that stays open.
|
|
14
|
+
*/
|
|
15
|
+
import type { ReactNode } from 'react';
|
|
16
|
+
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
|
|
17
|
+
import type { InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
18
|
+
import type { SidebarRightGuideBox } from '../../tab-registry.ts';
|
|
19
|
+
/** What the guide body needs from its host beyond the framework shares. */
|
|
20
|
+
export interface GuideInjected {
|
|
21
|
+
/** The registry's guide entries in `order`; observable, so a type registering later appears. */
|
|
22
|
+
readonly hooks: {
|
|
23
|
+
readonly guideEntries: ObservableSnapshot<readonly SidebarRightGuideBox[]>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** The guide body's composed props: the tab it draws, its chain child, its copy, and the entries. */
|
|
27
|
+
export type GuideBodyProps = PropsRuntime<'sidebar.right.pane.tab'> & PropsRenderSlots<'sidebar.right.tab.guide'> & PropsLocale<'sidebarRight'> & InjectFace<GuideInjected>;
|
|
28
|
+
/** The guide tab's body, replaceable through its chain child. */
|
|
29
|
+
export declare function GuideBody({ useTabInfo, useGuideEntries, renderSlotChain, t }: GuideBodyProps): ReactNode;
|
|
30
|
+
//# sourceMappingURL=GuideBody.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage one of the guide type's registration: what it IS.
|
|
3
|
+
*/
|
|
4
|
+
import type { TranslateNS } from '@deepseek-ai/dsh-client-locale/client';
|
|
5
|
+
import type { SidebarRightTabDefinition } from '../../tab-registry.ts';
|
|
6
|
+
/** The shipped guide implementation's identity: the key its body registers under. */
|
|
7
|
+
export declare const GUIDE_ID = "@deepseek-ai/dsh-client-ui-sidebar-right/guide";
|
|
8
|
+
/**
|
|
9
|
+
* The guide type's registry definition.
|
|
10
|
+
*
|
|
11
|
+
* A page type: it recognizes no resource address, because a guide views
|
|
12
|
+
* nothing, and is opened by kind; `builtin` is the ordinary band for a type
|
|
13
|
+
* shipped here.
|
|
14
|
+
* @param t - namespace-bound translate, read fresh on every title call.
|
|
15
|
+
* @returns the definition to register.
|
|
16
|
+
*/
|
|
17
|
+
export declare function guideDefinition(t: TranslateNS<'sidebarRight'>): SidebarRightTabDefinition;
|
|
18
|
+
//# sourceMappingURL=definition.d.ts.map
|