nexus-shell 0.2.9 → 0.2.10
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/README.md +33 -5
- package/dist/components/layout/PaneHost.d.ts +56 -0
- package/dist/components/layout/ShellLayout.d.ts +11 -1
- package/dist/components/widgets/ActivityBar.d.ts +14 -5
- package/dist/components/widgets/ChatPane.d.ts +17 -3
- package/dist/components/widgets/SidebarPane.d.ts +25 -6
- package/dist/components/widgets/TerminalPane.d.ts +21 -4
- package/dist/connected/ConnectedActivityBar.d.ts +12 -1
- package/dist/connected/ConnectedChatPane.d.ts +8 -5
- package/dist/connected/ConnectedSidebarPane.d.ts +23 -2
- package/dist/connected/ConnectedTerminalPane.d.ts +7 -4
- package/dist/connected/index.d.ts +3 -2
- package/dist/connected/panels.d.ts +28 -0
- package/dist/core/Boot.d.ts +18 -0
- package/dist/core/services/SidebarService.d.ts +34 -3
- package/dist/core/services/TerminalService.d.ts +8 -3
- package/dist/index.d.ts +4 -4
- package/dist/nexus-shell.es.js +2391 -2330
- package/dist/nexus-shell.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/dist/core/services/RightSidebarService.d.ts +0 -7
package/README.md
CHANGED
|
@@ -28,8 +28,10 @@ import { Boxes, Files } from 'lucide-react';
|
|
|
28
28
|
import {
|
|
29
29
|
AppTitle,
|
|
30
30
|
ShellLayout,
|
|
31
|
+
chatPanel,
|
|
31
32
|
componentRegistry,
|
|
32
33
|
initializeShell,
|
|
34
|
+
terminalPanel,
|
|
33
35
|
useLayoutStore,
|
|
34
36
|
} from 'nexus-shell';
|
|
35
37
|
import 'nexus-shell/style.css';
|
|
@@ -38,7 +40,13 @@ import 'nexus-shell/style.css';
|
|
|
38
40
|
componentRegistry.register('editor', Editor);
|
|
39
41
|
|
|
40
42
|
initializeShell({
|
|
41
|
-
panels: [
|
|
43
|
+
panels: [
|
|
44
|
+
{ id: 'files', label: 'Explorer', icon: Files, component: FileExplorer },
|
|
45
|
+
terminalPanel(),
|
|
46
|
+
],
|
|
47
|
+
// Chat on the right rail. Move it to `panels` for the left, or register
|
|
48
|
+
// the component as a tab — nothing about it is bound to one edge.
|
|
49
|
+
inspectorPanels: [chatPanel()],
|
|
42
50
|
commands: [
|
|
43
51
|
{ id: 'file.save', label: 'File: Save', keybinding: 'Control+s', execute: save },
|
|
44
52
|
],
|
|
@@ -56,6 +64,23 @@ createRoot(document.getElementById('root')!).render(
|
|
|
56
64
|
That is a working IDE: dockable tabs, a command palette on `Cmd/Ctrl+Shift+P`, a
|
|
57
65
|
terminal, a chat pane, and a themable frame.
|
|
58
66
|
|
|
67
|
+
## Where things go
|
|
68
|
+
|
|
69
|
+
The shell has four places a view can live, and nothing is nailed to one of
|
|
70
|
+
them. Chat and the terminal are ordinary registrations, so they go wherever you
|
|
71
|
+
put them — including into a dockable tab, alongside your own views.
|
|
72
|
+
|
|
73
|
+
| Place | How |
|
|
74
|
+
| :-- | :-- |
|
|
75
|
+
| Left rail | `panels: [...]` |
|
|
76
|
+
| Right rail | `inspectorPanels: [...]` |
|
|
77
|
+
| Dockable tab | `componentRegistry.register(id, Component)` + `addTab(id)` |
|
|
78
|
+
| Bottom of the workspace | A tab in the bottom row of your `initialLayoutJson` |
|
|
79
|
+
|
|
80
|
+
Anything hosted in a pane or a tab is told so, and drops its own title bar —
|
|
81
|
+
`SidebarPane` and the tab strip already draw one. Pass `chrome` explicitly to
|
|
82
|
+
force it either way.
|
|
83
|
+
|
|
59
84
|
## Growing an app
|
|
60
85
|
|
|
61
86
|
You add capability by **registering** it. None of this changes the layout, which
|
|
@@ -67,6 +92,8 @@ is what lets plugins and distant code contribute to the shell.
|
|
|
67
92
|
| An action or hotkey | `commandRegistry.registerCommand({ id, label, keybinding, execute })` — appears in the palette automatically |
|
|
68
93
|
| A menu entry | `menuRegistry.registerMenu('File', { id, label, commandId })` |
|
|
69
94
|
| A sidebar panel | Add it to `panels`; it becomes an activity bar icon |
|
|
95
|
+
| An inspector or properties panel | Add it to `inspectorPanels`; it docks right, with its own rail |
|
|
96
|
+
| Chat or a terminal, anywhere | `chatPanel()` / `terminalPanel()` in `panels` or `inspectorPanels`, or register the component as a tab |
|
|
70
97
|
| A status bar item | `useStatusBarStore.getState().addWidget(...)` |
|
|
71
98
|
| A dialog from anywhere | `await useModalStore.getState().openConfirm('Sure?')` |
|
|
72
99
|
| An unsaved-changes guard | `useLayoutStore.getState().setTabDirty(tabId, true)` |
|
|
@@ -114,8 +141,8 @@ shell's own frame and keep every registration working.
|
|
|
114
141
|
|
|
115
142
|
| Component | What it is |
|
|
116
143
|
| :-- | :-- |
|
|
117
|
-
| `ActivityBar` | Vertical icon rail for switching
|
|
118
|
-
| `ChatPane` |
|
|
144
|
+
| `ActivityBar` | Vertical icon rail for switching side panels, either edge |
|
|
145
|
+
| `ChatPane` | Chat transcript with slash-command autocomplete; fills any host |
|
|
119
146
|
| `CommandPalette` | Filterable, keyboard-navigable command list |
|
|
120
147
|
| `ContextMenu` | Floating menu positioned at viewport coordinates |
|
|
121
148
|
| `DataGrid` | Sortable, filterable table with optional virtualisation |
|
|
@@ -124,9 +151,10 @@ shell's own frame and keep every registration working.
|
|
|
124
151
|
| `QuickSearch` | Compact search field with grouped results, for the menu bar |
|
|
125
152
|
| `SearchWidget` | Full-height search panel with autocomplete, for the sidebar |
|
|
126
153
|
| `SettingsPanel` | Theme picker body for the settings sidebar |
|
|
127
|
-
| `SidebarPane` | Titled, closable
|
|
154
|
+
| `SidebarPane` | Titled, closable side panel; `side` docks it left or right |
|
|
128
155
|
| `StatusBar` | Footer with left / center / right item groups |
|
|
129
|
-
| `TerminalPane` |
|
|
156
|
+
| `TerminalPane` | Terminal log and input; fills any host |
|
|
157
|
+
| `PaneHostProvider` | Tells hosted components a title bar is already drawn |
|
|
130
158
|
| `ThemeSwitcher` | Segmented theme control |
|
|
131
159
|
| `TreeWidget` | Virtualised tree for any hierarchy, with drag-to-move and a data-driven menu |
|
|
132
160
|
| `GraphCanvas` | Infinite pannable, zoomable field for node-and-edge editing |
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What a component can learn about the thing hosting it.
|
|
5
|
+
*
|
|
6
|
+
* A pane, a dockable tab and a bare `<div>` give a component very different
|
|
7
|
+
* surroundings, and the difference that matters is whether a title bar has
|
|
8
|
+
* already been drawn. Without this, a chat pane inside a `SidebarPane` renders
|
|
9
|
+
* two headers and two close buttons.
|
|
10
|
+
*/
|
|
11
|
+
export interface IPaneHost {
|
|
12
|
+
/**
|
|
13
|
+
* The host already draws a title bar and a close affordance, so a hosted
|
|
14
|
+
* component should not draw its own.
|
|
15
|
+
*/
|
|
16
|
+
chrome: boolean;
|
|
17
|
+
/** Where the host sits, when it knows. Useful for choosing a layout. */
|
|
18
|
+
placement?: 'left' | 'right' | 'bottom' | 'tab';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Read the surrounding host, or `null` when there isn't one.
|
|
22
|
+
*
|
|
23
|
+
* Prefer {@link useHostChrome}, which folds in an explicit prop.
|
|
24
|
+
*/
|
|
25
|
+
export declare const usePaneHost: () => IPaneHost | null;
|
|
26
|
+
/**
|
|
27
|
+
* Whether this component should draw its own title bar.
|
|
28
|
+
*
|
|
29
|
+
* The host provides the *default*; an explicit prop always wins. That ordering
|
|
30
|
+
* is deliberate — auto-detection keeps the common case free of configuration,
|
|
31
|
+
* but a component that can only be told implicitly is one you cannot unit-test
|
|
32
|
+
* without building a host around it, and cannot force when the detection is
|
|
33
|
+
* wrong.
|
|
34
|
+
*
|
|
35
|
+
* @param explicit The component's own `chrome` prop, if the caller set one.
|
|
36
|
+
*/
|
|
37
|
+
export declare const useHostChrome: (explicit?: boolean) => boolean;
|
|
38
|
+
export interface PaneHostProviderProps extends IPaneHost {
|
|
39
|
+
/** Everything that should see this host. */
|
|
40
|
+
children: ReactNode;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Declare that everything inside is hosted.
|
|
44
|
+
*
|
|
45
|
+
* `SidebarPane` and the shell's tab factory do this for you. Wrap your own
|
|
46
|
+
* container in it if you draw a title bar and want hosted components to defer.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```tsx
|
|
50
|
+
* <PaneHostProvider chrome placement="bottom">
|
|
51
|
+
* <MyTitleBar />
|
|
52
|
+
* <ConnectedTerminalPane />
|
|
53
|
+
* </PaneHostProvider>
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare const PaneHostProvider: ({ children, ...host }: PaneHostProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -11,6 +11,16 @@ export interface ShellLayoutProps {
|
|
|
11
11
|
* renders its `component` in the sidebar when selected.
|
|
12
12
|
*/
|
|
13
13
|
panels?: ISidebarPanel[];
|
|
14
|
+
/**
|
|
15
|
+
* Right-hand panels — inspectors, properties, anything about the current
|
|
16
|
+
* selection rather than about navigation.
|
|
17
|
+
*
|
|
18
|
+
* Registered exactly like `panels`, against a separate store, so the two
|
|
19
|
+
* sides open and close independently: file tree on the left and properties on
|
|
20
|
+
* the right, both at once. The right-hand rail appears only once at least one
|
|
21
|
+
* is registered.
|
|
22
|
+
*/
|
|
23
|
+
inspectorPanels?: ISidebarPanel[];
|
|
14
24
|
/** Slash commands available in the chat pane. */
|
|
15
25
|
slashCommands?: ISlashCommandConfig[];
|
|
16
26
|
/** Menu structure, keyed by top-level menu name. Items dispatch by `commandId`. */
|
|
@@ -92,4 +102,4 @@ export interface ShellLayoutProps {
|
|
|
92
102
|
* );
|
|
93
103
|
* ```
|
|
94
104
|
*/
|
|
95
|
-
export declare const ShellLayout: ({ panels, slashCommands, menuConfig, statusBarConfig, title, rightMenuBarContent, centerMenuBarContent, layoutModel, initialLayoutJson, disableLocalStorage, onLayoutChange, }: ShellLayoutProps) => import("react/jsx-runtime").JSX.Element;
|
|
105
|
+
export declare const ShellLayout: ({ panels, inspectorPanels, slashCommands, menuConfig, statusBarConfig, title, rightMenuBarContent, centerMenuBarContent, layoutModel, initialLayoutJson, disableLocalStorage, onLayoutChange, }: ShellLayoutProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LucideIcon } from 'lucide-react';
|
|
2
|
+
import { PaneSide } from './SidebarPane';
|
|
2
3
|
|
|
3
4
|
/** A single icon button in the {@link ActivityBar}. */
|
|
4
5
|
export interface IActivityBarItem {
|
|
@@ -24,17 +25,25 @@ export interface ActivityBarProps {
|
|
|
24
25
|
* deselect it — is the caller's decision, not the component's.
|
|
25
26
|
*/
|
|
26
27
|
onSelect?: (id: string) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Which edge of the shell the rail is docked to. Defaults to `"left"`.
|
|
30
|
+
*
|
|
31
|
+
* The divider and the active-item indicator both mirror, so a right-hand rail
|
|
32
|
+
* marks its selection on the edge facing the content.
|
|
33
|
+
*/
|
|
34
|
+
side?: PaneSide;
|
|
27
35
|
/** Extra classes merged onto the root `<aside>`. */
|
|
28
36
|
className?: string;
|
|
29
37
|
/** Accessible label for the landmark. Defaults to `"Activity Bar"`. */
|
|
30
38
|
'aria-label'?: string;
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
33
|
-
* The narrow vertical icon rail
|
|
34
|
-
*
|
|
41
|
+
* The narrow vertical icon rail at the edge of the shell, used to switch which
|
|
42
|
+
* panel the adjacent {@link SidebarPane} shows.
|
|
35
43
|
*
|
|
36
|
-
* Controlled — it holds no selection state.
|
|
37
|
-
* by `ShellLayout`, see `ConnectedActivityBar
|
|
44
|
+
* Controlled — it holds no selection state. `side` docks it left or right; for
|
|
45
|
+
* the store-backed variants used by `ShellLayout`, see `ConnectedActivityBar`
|
|
46
|
+
* and `ConnectedInspectorBar`.
|
|
38
47
|
*
|
|
39
48
|
* @example
|
|
40
49
|
* ```tsx
|
|
@@ -45,4 +54,4 @@ export interface ActivityBarProps {
|
|
|
45
54
|
* />
|
|
46
55
|
* ```
|
|
47
56
|
*/
|
|
48
|
-
export declare const ActivityBar: ({ items, bottomItems, activeId, onSelect, className, "aria-label": ariaLabel, }: ActivityBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export declare const ActivityBar: ({ items, bottomItems, activeId, onSelect, side, className, "aria-label": ariaLabel, }: ActivityBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -38,17 +38,31 @@ export interface ChatPaneProps {
|
|
|
38
38
|
placeholder?: string;
|
|
39
39
|
/** Shown in place of the transcript when `messages` is empty. */
|
|
40
40
|
emptyState?: React.ReactNode;
|
|
41
|
+
/**
|
|
42
|
+
* Draw the title bar.
|
|
43
|
+
*
|
|
44
|
+
* Left unset, this follows the host: inside a `SidebarPane` or a dockable tab
|
|
45
|
+
* the title is already drawn, so the pane omits its own. Set it explicitly to
|
|
46
|
+
* force either way — which is also what lets the component be tested without
|
|
47
|
+
* a host around it.
|
|
48
|
+
*/
|
|
49
|
+
chrome?: boolean;
|
|
41
50
|
/** Extra classes merged onto the root element. */
|
|
42
51
|
className?: string;
|
|
43
52
|
}
|
|
44
53
|
/**
|
|
45
|
-
* A
|
|
54
|
+
* A chat panel: a scrolling transcript over a composer with slash-command
|
|
46
55
|
* autocomplete.
|
|
47
56
|
*
|
|
48
57
|
* Fully controlled — it owns only the in-progress input and the suggestion
|
|
49
58
|
* highlight. Messages, sending and visibility belong to the caller, so the same
|
|
50
59
|
* component works against a local array, a websocket, or an LLM endpoint. For
|
|
51
|
-
* the store-backed variant
|
|
60
|
+
* the store-backed variant, see `ConnectedChatPane`.
|
|
61
|
+
*
|
|
62
|
+
* **It fills whatever hosts it.** Register it as a sidebar panel, an inspector
|
|
63
|
+
* panel or a dockable tab and it takes that shape; it carries no width or edge
|
|
64
|
+
* of its own. Inside a host that draws a title bar it omits its own header —
|
|
65
|
+
* see `chrome` to override.
|
|
52
66
|
*
|
|
53
67
|
* @example
|
|
54
68
|
* ```tsx
|
|
@@ -60,4 +74,4 @@ export interface ChatPaneProps {
|
|
|
60
74
|
* />
|
|
61
75
|
* ```
|
|
62
76
|
*/
|
|
63
|
-
export declare const ChatPane: ({ messages, onSend, slashCommands, onSlashCommand, onClose, title, placeholder, emptyState, className, }: ChatPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
77
|
+
export declare const ChatPane: ({ messages, onSend, slashCommands, onSlashCommand, onClose, title, placeholder, emptyState, chrome, className, }: ChatPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
+
/** Which edge of the shell a pane or rail is docked to. */
|
|
4
|
+
export type PaneSide = 'left' | 'right';
|
|
3
5
|
export interface SidebarPaneProps {
|
|
4
6
|
/**
|
|
5
7
|
* Heading text. Rendered uppercase with letter-spacing; pass it in natural
|
|
@@ -15,23 +17,40 @@ export interface SidebarPaneProps {
|
|
|
15
17
|
onClose?: () => void;
|
|
16
18
|
/** Fixed pane width, as a CSS length. Defaults to `"300px"`. */
|
|
17
19
|
width?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Which edge of the shell the pane is docked to. Defaults to `"left"`.
|
|
22
|
+
*
|
|
23
|
+
* Only the divider moves — a right-hand pane is the same container, so an
|
|
24
|
+
* inspector does not need a component of its own.
|
|
25
|
+
*/
|
|
26
|
+
side?: PaneSide;
|
|
18
27
|
/** Extra classes merged onto the root `<aside>`. */
|
|
19
28
|
className?: string;
|
|
20
29
|
}
|
|
21
30
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
31
|
+
* A docked side panel: a titled header with an optional close button, over a
|
|
32
|
+
* scrolling body.
|
|
24
33
|
*
|
|
25
34
|
* A pure container — it decides nothing about which panel is showing and holds
|
|
26
|
-
* no state. Render whatever you like as `children`.
|
|
27
|
-
*
|
|
28
|
-
* `
|
|
35
|
+
* no state. Render whatever you like as `children`.
|
|
36
|
+
*
|
|
37
|
+
* `side` docks it left (beside the {@link ActivityBar}) or right, where it
|
|
38
|
+
* becomes an inspector. Both are the same component deliberately: before this
|
|
39
|
+
* prop existed every application hand-rolled its own right-hand
|
|
40
|
+
* `<div className="w-80 border-l …">`, and they all drifted apart.
|
|
41
|
+
*
|
|
42
|
+
* For the store-backed variants used by `ShellLayout`, see
|
|
43
|
+
* `ConnectedSidebarPane` and `ConnectedInspectorPane`.
|
|
29
44
|
*
|
|
30
45
|
* @example
|
|
31
46
|
* ```tsx
|
|
32
47
|
* <SidebarPane title="Explorer" onClose={() => setActive(null)}>
|
|
33
48
|
* <TreeWidget data={files} />
|
|
34
49
|
* </SidebarPane>
|
|
50
|
+
*
|
|
51
|
+
* <SidebarPane title="Properties" side="right" width="320px">
|
|
52
|
+
* <PropertyPanel subjects={selected} fields={fields} />
|
|
53
|
+
* </SidebarPane>
|
|
35
54
|
* ```
|
|
36
55
|
*/
|
|
37
|
-
export declare const SidebarPane: ({ title, children, onClose, width, className, }: SidebarPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
56
|
+
export declare const SidebarPane: ({ title, children, onClose, width, side, className, }: SidebarPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -12,8 +12,22 @@ export interface TerminalPaneProps {
|
|
|
12
12
|
onCommand?: (command: string) => void;
|
|
13
13
|
/** Called when the collapse or close button is pressed. */
|
|
14
14
|
onClose?: () => void;
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Height of the pane, as a CSS length.
|
|
17
|
+
*
|
|
18
|
+
* Leave it unset inside a host that sizes its own children — a side pane or a
|
|
19
|
+
* dockable tab — and the terminal fills the space instead. Defaults to
|
|
20
|
+
* `"250px"` only when standalone.
|
|
21
|
+
*/
|
|
16
22
|
height?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Draw the title bar.
|
|
25
|
+
*
|
|
26
|
+
* Left unset, this follows the host: inside a `SidebarPane` or a dockable tab
|
|
27
|
+
* the title is already drawn, so the pane omits its own. Set it explicitly to
|
|
28
|
+
* force either way.
|
|
29
|
+
*/
|
|
30
|
+
chrome?: boolean;
|
|
17
31
|
/** Prompt string shown before the input. Defaults to `"$"`. */
|
|
18
32
|
prompt?: string;
|
|
19
33
|
/** Title shown in the pane header. Defaults to `"Terminal"`. */
|
|
@@ -22,14 +36,17 @@ export interface TerminalPaneProps {
|
|
|
22
36
|
className?: string;
|
|
23
37
|
}
|
|
24
38
|
/**
|
|
25
|
-
* A
|
|
26
|
-
* input.
|
|
39
|
+
* A terminal pane: a scrolling output log over a single-line input.
|
|
27
40
|
*
|
|
28
41
|
* Presentational and fully controlled — it owns only the in-progress input
|
|
29
42
|
* text. Visibility, history and command execution all belong to the caller.
|
|
30
43
|
* For the store-backed variant with built-in `clear`/`help` handling, see
|
|
31
44
|
* `ConnectedTerminalPane`.
|
|
32
45
|
*
|
|
46
|
+
* **It fills whatever hosts it.** Bottom of the workspace, a side pane, or a
|
|
47
|
+
* dockable tab — register it wherever it belongs. Inside a host that draws a
|
|
48
|
+
* title bar it omits its own header and its own fixed height.
|
|
49
|
+
*
|
|
33
50
|
* @example
|
|
34
51
|
* ```tsx
|
|
35
52
|
* const [history, setHistory] = useState<string[]>([]);
|
|
@@ -40,4 +57,4 @@ export interface TerminalPaneProps {
|
|
|
40
57
|
* />
|
|
41
58
|
* ```
|
|
42
59
|
*/
|
|
43
|
-
export declare const TerminalPane: ({ history, onCommand, onClose, height, prompt, title, className, }: TerminalPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
60
|
+
export declare const TerminalPane: ({ history, onCommand, onClose, height, prompt, title, chrome, className, }: TerminalPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,9 +2,20 @@ import { ActivityBarProps } from '../components/widgets/ActivityBar';
|
|
|
2
2
|
|
|
3
3
|
export type ConnectedActivityBarProps = Omit<ActivityBarProps, 'items' | 'activeId' | 'onSelect'>;
|
|
4
4
|
/**
|
|
5
|
-
* {@link ActivityBar} bound to `useSidebarStore
|
|
5
|
+
* {@link ActivityBar} bound to `useSidebarStore`, docked left.
|
|
6
6
|
*
|
|
7
7
|
* Derives its items from the registered sidebar panels and toggles the active
|
|
8
8
|
* panel on click — clicking the already-active item closes the sidebar.
|
|
9
9
|
*/
|
|
10
10
|
export declare const ConnectedActivityBar: (props: ConnectedActivityBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
/**
|
|
12
|
+
* {@link ActivityBar} bound to `useInspectorStore`, docked right.
|
|
13
|
+
*
|
|
14
|
+
* Rendered only when inspector panels are registered — an empty rail is a
|
|
15
|
+
* twelve-pixel strip of nothing, and a shell that grows one the moment you
|
|
16
|
+
* register a panel is easier to reason about than one you must switch on.
|
|
17
|
+
*
|
|
18
|
+
* It also defaults to no bottom group: settings live on the left rail, and two
|
|
19
|
+
* settings buttons would be a puzzle.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ConnectedInspectorBar: (props: ConnectedActivityBarProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -9,10 +9,13 @@ export type ConnectedChatPaneProps = Omit<ChatPaneProps, 'messages' | 'slashComm
|
|
|
9
9
|
onSend?: (text: string) => void;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
* {@link ChatPane} bound to `useChatStore` and `
|
|
12
|
+
* {@link ChatPane} bound to `useChatStore` and `useChatVisibilityStore`.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Appends each submission to the transcript, then either dispatches a
|
|
15
|
+
* registered slash command's `execute` or hands the text to `onSend`.
|
|
16
|
+
*
|
|
17
|
+
* Visibility belongs to whatever hosts it — a pane closes by closing the pane,
|
|
18
|
+
* a tab by closing the tab. Register it with `chatPanel()`, or as a tab
|
|
19
|
+
* component, and put it wherever it belongs.
|
|
17
20
|
*/
|
|
18
|
-
export declare const ConnectedChatPane: ({ onSend, ...props }: ConnectedChatPaneProps) => import("react/jsx-runtime").JSX.Element
|
|
21
|
+
export declare const ConnectedChatPane: ({ onSend, ...props }: ConnectedChatPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,11 +2,32 @@ import { SidebarPaneProps } from '../components/widgets/SidebarPane';
|
|
|
2
2
|
|
|
3
3
|
export type ConnectedSidebarPaneProps = Omit<SidebarPaneProps, 'title' | 'children' | 'onClose'>;
|
|
4
4
|
/**
|
|
5
|
-
* {@link SidebarPane} bound to `useSidebarStore
|
|
5
|
+
* {@link SidebarPane} bound to `useSidebarStore`, docked left.
|
|
6
6
|
*
|
|
7
7
|
* Renders whichever panel is active, or nothing when the sidebar is closed. The
|
|
8
8
|
* reserved `"settings"` panel id renders the built-in {@link SettingsPanel}
|
|
9
9
|
* wired to `useThemeStore`; every other id resolves against the panels
|
|
10
10
|
* registered in the store.
|
|
11
11
|
*/
|
|
12
|
-
export declare const ConnectedSidebarPane: (props: ConnectedSidebarPaneProps) => import("react/jsx-runtime").JSX.Element
|
|
12
|
+
export declare const ConnectedSidebarPane: (props: ConnectedSidebarPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
/**
|
|
14
|
+
* {@link SidebarPane} bound to `useInspectorStore`, docked right.
|
|
15
|
+
*
|
|
16
|
+
* The same pane on the other edge, resolving against a separate panel registry
|
|
17
|
+
* — so an inspector opens and closes independently of the explorer, which is
|
|
18
|
+
* how anyone actually works: file tree open on the left, properties on the
|
|
19
|
+
* right, both at once.
|
|
20
|
+
*
|
|
21
|
+
* Unlike the left pane there is no reserved `"settings"` id here; settings are
|
|
22
|
+
* navigation, and navigation belongs on the left.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <ShellLayout
|
|
27
|
+
* inspectorPanels={[
|
|
28
|
+
* { id: 'properties', label: 'Properties', icon: Sliders, component: Inspector },
|
|
29
|
+
* ]}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare const ConnectedInspectorPane: (props: ConnectedSidebarPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -11,8 +11,11 @@ export type ConnectedTerminalPaneProps = Omit<TerminalPaneProps, 'history' | 'on
|
|
|
11
11
|
/**
|
|
12
12
|
* {@link TerminalPane} bound to `useTerminalStore`.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Echoes each command into history and handles `help` and `clear` internally;
|
|
15
|
+
* anything else is offered to `onCommand` before falling back to a not-found
|
|
16
|
+
* message.
|
|
17
|
+
*
|
|
18
|
+
* Visibility belongs to whatever hosts it. Register it with `terminalPanel()`
|
|
19
|
+
* for a side pane, or as a tab component for the docking area.
|
|
17
20
|
*/
|
|
18
|
-
export declare const ConnectedTerminalPane: ({ onCommand, ...props }: ConnectedTerminalPaneProps) => import("react/jsx-runtime").JSX.Element
|
|
21
|
+
export declare const ConnectedTerminalPane: ({ onCommand, ...props }: ConnectedTerminalPaneProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
* Reach for these when you want the shell's behavior. Reach for the plain
|
|
10
10
|
* components when you want control.
|
|
11
11
|
*/
|
|
12
|
-
export {
|
|
12
|
+
export { chatPanel, terminalPanel } from './panels';
|
|
13
|
+
export { ConnectedActivityBar, ConnectedInspectorBar, type ConnectedActivityBarProps, } from './ConnectedActivityBar';
|
|
13
14
|
export { ConnectedChatPane, type ConnectedChatPaneProps } from './ConnectedChatPane';
|
|
14
15
|
export { ConnectedCommandPalette, type ConnectedCommandPaletteProps } from './ConnectedCommandPalette';
|
|
15
16
|
export { ConnectedMenuBar, type ConnectedMenuBarProps } from './ConnectedMenuBar';
|
|
16
17
|
export { ConnectedModal, type ConnectedModalProps } from './ConnectedModal';
|
|
17
|
-
export { ConnectedSidebarPane, type ConnectedSidebarPaneProps } from './ConnectedSidebarPane';
|
|
18
|
+
export { ConnectedInspectorPane, ConnectedSidebarPane, type ConnectedSidebarPaneProps, } from './ConnectedSidebarPane';
|
|
18
19
|
export { ConnectedStatusBar, type ConnectedStatusBarProps } from './ConnectedStatusBar';
|
|
19
20
|
export { ConnectedTerminalPane, type ConnectedTerminalPaneProps } from './ConnectedTerminalPane';
|
|
20
21
|
export { ConnectedThemeSwitcher, type ConnectedThemeSwitcherProps } from './ConnectedThemeSwitcher';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CHAT_PANEL_ID, TERMINAL_PANEL_ID, ISidebarPanel } from '../core/services/SidebarService';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ready-made panel descriptors for the shell's own pieces.
|
|
5
|
+
*
|
|
6
|
+
* Chat and terminal used to be nailed into fixed slots — chat on the right,
|
|
7
|
+
* terminal along the bottom — and there was no way to put them anywhere else.
|
|
8
|
+
* They are now ordinary registrations, so they go wherever you register them:
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* initializeShell({
|
|
12
|
+
* panels: [terminalPanel()], // left rail
|
|
13
|
+
* inspectorPanels: [chatPanel()], // right rail
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // …or as a dockable tab, alongside everything else:
|
|
17
|
+
* componentRegistry.register('terminal', ConnectedTerminalPane);
|
|
18
|
+
* useLayoutStore.getState().addTab('terminal', 'Terminal');
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Each takes overrides, so the id, label or icon can be changed without
|
|
22
|
+
* rebuilding the descriptor by hand.
|
|
23
|
+
*/
|
|
24
|
+
/** The chat pane, as a registerable panel. */
|
|
25
|
+
export declare const chatPanel: (overrides?: Partial<ISidebarPanel>) => ISidebarPanel;
|
|
26
|
+
/** The terminal, as a registerable panel. */
|
|
27
|
+
export declare const terminalPanel: (overrides?: Partial<ISidebarPanel>) => ISidebarPanel;
|
|
28
|
+
export { CHAT_PANEL_ID, TERMINAL_PANEL_ID };
|
package/dist/core/Boot.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ export interface InitializeShellOptions {
|
|
|
10
10
|
* renders its `component` in the sidebar when selected.
|
|
11
11
|
*/
|
|
12
12
|
panels?: ISidebarPanel[];
|
|
13
|
+
/**
|
|
14
|
+
* Right-hand panels — inspectors and properties, as opposed to navigation.
|
|
15
|
+
* Registered against `useInspectorStore`, so they open and close
|
|
16
|
+
* independently of the left-hand sidebar.
|
|
17
|
+
*/
|
|
18
|
+
inspectorPanels?: ISidebarPanel[];
|
|
13
19
|
/** Menus keyed by top-level name. Items dispatch through their `commandId`. */
|
|
14
20
|
menus?: Record<string, IMenuItemConfig[]>;
|
|
15
21
|
/** Commands available to the palette, menus and keybindings. */
|
|
@@ -32,6 +38,18 @@ export interface InitializeShellOptions {
|
|
|
32
38
|
}
|
|
33
39
|
/** The command ids `initializeShell` registers unless told not to. */
|
|
34
40
|
export declare const DEFAULT_COMMAND_IDS: readonly ["view.toggleTerminal", "view.toggleChat", "view.toggleSidebar", ...string[]];
|
|
41
|
+
/**
|
|
42
|
+
* Toggle a panel wherever it happens to be registered.
|
|
43
|
+
*
|
|
44
|
+
* Chat and terminal are no longer nailed to one edge, so a command that toggled
|
|
45
|
+
* a fixed slot would be wrong the moment you moved them. This looks the id up
|
|
46
|
+
* in the left rail, then the right, and finally opens it as a tab — which is
|
|
47
|
+
* also what makes `Control+\`` keep working when the terminal is a tab.
|
|
48
|
+
*
|
|
49
|
+
* Returns `false` when the id is registered nowhere, so a caller can tell the
|
|
50
|
+
* difference between "toggled" and "nothing to toggle".
|
|
51
|
+
*/
|
|
52
|
+
export declare const togglePanel: (id: string) => boolean;
|
|
35
53
|
/**
|
|
36
54
|
* Boot the shell: register commands, menus, panels and status bar items into
|
|
37
55
|
* the shell registries and stores.
|
|
@@ -1,17 +1,48 @@
|
|
|
1
|
+
import { StoreApi, UseBoundStore } from 'zustand';
|
|
1
2
|
import { LucideIcon } from 'lucide-react';
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* The docked side panels, one store per edge of the shell.
|
|
6
|
+
*
|
|
7
|
+
* Both edges behave identically — a registry of panels, one of which is active,
|
|
8
|
+
* remembered across reloads — so they share an implementation rather than two
|
|
9
|
+
* that drift.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Ids the shell's own panels use by default.
|
|
13
|
+
*
|
|
14
|
+
* Declared here rather than beside the descriptors so `core` can resolve them
|
|
15
|
+
* without depending on `connected` — the dependency runs the other way.
|
|
16
|
+
*/
|
|
17
|
+
export declare const CHAT_PANEL_ID = "chat";
|
|
18
|
+
export declare const TERMINAL_PANEL_ID = "terminal";
|
|
3
19
|
export interface ISidebarPanel {
|
|
4
20
|
id: string;
|
|
5
21
|
label: string;
|
|
6
22
|
icon: LucideIcon;
|
|
7
23
|
component: React.ComponentType<any> | React.ReactNode;
|
|
8
24
|
}
|
|
9
|
-
interface SidebarState {
|
|
25
|
+
export interface SidebarState {
|
|
26
|
+
/** Id of the open panel, or `null` when the pane is collapsed. */
|
|
10
27
|
activeSidebar: string | null;
|
|
11
28
|
panels: ISidebarPanel[];
|
|
12
29
|
setActiveSidebar: (id: string | null) => void;
|
|
30
|
+
/** Open the panel, or close it if it is already open. */
|
|
13
31
|
toggleSidebar: (id: string) => void;
|
|
14
32
|
setPanels: (panels: ISidebarPanel[]) => void;
|
|
15
33
|
}
|
|
16
|
-
export
|
|
17
|
-
|
|
34
|
+
export type SidebarStore = UseBoundStore<StoreApi<SidebarState>>;
|
|
35
|
+
/**
|
|
36
|
+
* A panel store bound to one storage key.
|
|
37
|
+
*
|
|
38
|
+
* Exported so an application with a third docked region — a bottom drawer, say
|
|
39
|
+
* — gets the same behaviour without copying it.
|
|
40
|
+
*/
|
|
41
|
+
export declare const createSidebarStore: (storageKey: string) => SidebarStore;
|
|
42
|
+
/** The left-hand pane, beside the activity bar. */
|
|
43
|
+
export declare const useSidebarStore: SidebarStore;
|
|
44
|
+
/**
|
|
45
|
+
* The right-hand pane: inspectors, properties, chat transcripts — anything
|
|
46
|
+
* about the current selection rather than about navigation.
|
|
47
|
+
*/
|
|
48
|
+
export declare const useInspectorStore: SidebarStore;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal's scrollback.
|
|
3
|
+
*
|
|
4
|
+
* Visibility used to live here too, back when the terminal had exactly one
|
|
5
|
+
* place it could be. It is now registered like any other panel, so whatever
|
|
6
|
+
* hosts it owns whether it is showing — see `terminalPanel()` and
|
|
7
|
+
* `togglePanel()`.
|
|
8
|
+
*/
|
|
1
9
|
interface TerminalState {
|
|
2
|
-
isOpen: boolean;
|
|
3
10
|
history: string[];
|
|
4
|
-
setOpen: (open: boolean) => void;
|
|
5
|
-
toggle: () => void;
|
|
6
11
|
addHistory: (line: string) => void;
|
|
7
12
|
clearHistory: () => void;
|
|
8
13
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
* to.
|
|
24
24
|
*/
|
|
25
25
|
export { ShellLayout, ShellLayout as NexusWorkspaceShell, type ShellLayoutProps } from './components/layout/ShellLayout';
|
|
26
|
-
export { initializeShell, DEFAULT_COMMAND_IDS, type InitializeShellOptions, } from './core/Boot';
|
|
26
|
+
export { initializeShell, togglePanel, DEFAULT_COMMAND_IDS, type InitializeShellOptions, } from './core/Boot';
|
|
27
|
+
export { PaneHostProvider, usePaneHost, useHostChrome, type IPaneHost, type PaneHostProviderProps, } from './components/layout/PaneHost';
|
|
27
28
|
export { commandRegistry, CommandRegistry, type ICommand } from './core/registry/CommandRegistry';
|
|
28
29
|
export { menuRegistry, MenuRegistry, type IMenuItemConfig } from './core/registry/MenuRegistry';
|
|
29
30
|
export { pluginRegistry, type IPlugin, type PluginStatus } from './core/registry/PluginRegistry';
|
|
@@ -31,8 +32,7 @@ export { componentRegistry, type ComponentConstructor } from './core/registry/Co
|
|
|
31
32
|
export { useChatStore, type ISlashCommandConfig } from './core/services/ChatService';
|
|
32
33
|
export { useLayoutStore } from './core/services/LayoutService';
|
|
33
34
|
export { useModalStore, type ModalType as ModalStoreType } from './core/services/ModalStoreService';
|
|
34
|
-
export {
|
|
35
|
-
export { useSidebarStore, type ISidebarPanel } from './core/services/SidebarService';
|
|
35
|
+
export { useSidebarStore, useInspectorStore, createSidebarStore, CHAT_PANEL_ID, TERMINAL_PANEL_ID, type ISidebarPanel, type SidebarState, type SidebarStore, } from './core/services/SidebarService';
|
|
36
36
|
export { useStatusBarStore, type IStatusBarWidgetConfig } from './core/services/StatusBarService';
|
|
37
37
|
export { useTerminalStore } from './core/services/TerminalService';
|
|
38
38
|
export { useThemeStore, type ThemeType } from './core/services/ThemeService';
|
|
@@ -50,7 +50,7 @@ export { Modal, type ModalProps, type ModalType } from './components/widgets/Mod
|
|
|
50
50
|
export { QuickSearch, type QuickSearchProps, type IQuickSearchResult } from './components/widgets/QuickSearch';
|
|
51
51
|
export { SearchWidget, type SearchWidgetProps, type ISearchResult } from './components/widgets/SearchWidget';
|
|
52
52
|
export { SettingsPanel, type SettingsPanelProps, type ISettingsThemeOption, DEFAULT_SETTINGS_THEMES } from './components/widgets/SettingsPanel';
|
|
53
|
-
export { SidebarPane, type SidebarPaneProps } from './components/widgets/SidebarPane';
|
|
53
|
+
export { SidebarPane, type SidebarPaneProps, type PaneSide } from './components/widgets/SidebarPane';
|
|
54
54
|
export { StatusBar, type StatusBarProps, type IStatusBarWidget } from './components/widgets/StatusBar';
|
|
55
55
|
export { TerminalPane, type TerminalPaneProps } from './components/widgets/TerminalPane';
|
|
56
56
|
export { ThemeSwitcher, type ThemeSwitcherProps, type IThemeOption, DEFAULT_THEME_OPTIONS } from './components/widgets/ThemeSwitcher';
|