nexus-shell 0.2.8 → 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.
@@ -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
- /** Height of the pane, as a CSS length. Defaults to `"250px"`. */
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 bottom-docked terminal pane: a scrolling output log over a single-line
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 `useRightSidebarStore`.
12
+ * {@link ChatPane} bound to `useChatStore` and `useChatVisibilityStore`.
13
13
  *
14
- * Renders nothing while the right sidebar reports chat closed. Appends each
15
- * submission to the transcript, then either dispatches a registered slash
16
- * command's `execute` or hands the text to `onSend`.
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 | null;
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 | null;
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
- * Renders nothing while the store reports the terminal closed. Echoes each
15
- * command into history and handles `help` and `clear` internally; anything else
16
- * is offered to `onCommand` before falling back to a not-found message.
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 | null;
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 { ConnectedActivityBar, type ConnectedActivityBarProps } from './ConnectedActivityBar';
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 };
@@ -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 declare const useSidebarStore: import('zustand').UseBoundStore<import('zustand').StoreApi<SidebarState>>;
17
- export {};
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 { useRightSidebarStore } from './core/services/RightSidebarService';
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';
@@ -59,5 +59,7 @@ export { UserProfile, type UserProfileProps, type IUserProfile, type IUserProfil
59
59
  export * from './components/graph';
60
60
  export { BUILT_IN_LAYOUTS, gridLayout, layeredLayout, type GraphLayout, type GridLayoutOptions, type LayeredLayoutOptions, type LayoutDirection, } from './lib/layout';
61
61
  export { DEFAULT_NODE_SIZE, IDENTITY_VIEWPORT, clampScale, edgesOf, findNeighbour, fitViewport, graphBounds, isConnected, nextId, nodeRect, placeRelativeTo, portPoint, rectCenter, removeNode, resolvePorts, toGraphSpace, toScreenSpace, unionRect, viewportRect, centerOn, zoomAt, type Direction, type GraphPort, type IGraphEdge, type IGraphNode, type IPoint, type IRect, type IViewport, } from './lib/graph';
62
+ export * from './components/properties';
63
+ export { getPath, setPath, readValue, readValues, writeValue, writeValues, type IPropertyAccessor, type IPropertyValue, } from './lib/properties';
62
64
  export { cn } from './lib/cn';
63
65
  export { BUNDLED_THEMES, BUNDLED_THEME_CLASSES, themeClass, type IBundledTheme, type BundledThemeId, } from './lib/themes';
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Reading and writing property values across one or many subjects.
3
+ *
4
+ * The awkward parts of a property panel are not the inputs — they are knowing
5
+ * what to show when several things are selected and their values disagree, and
6
+ * writing a nested value back without mutating anything. Both live here, as
7
+ * pure functions, so they can be tested without rendering.
8
+ */
9
+ /** A value shared by every selected subject, or a marker that they differ. */
10
+ export interface IPropertyValue<T = unknown> {
11
+ /** The common value, or `undefined` when subjects disagree or none are selected. */
12
+ value: T | undefined;
13
+ /**
14
+ * Subjects hold different values for this property.
15
+ *
16
+ * A field showing `mixed` must not display one subject's value as if it were
17
+ * everyone's — that is how a multi-selection edit silently flattens data.
18
+ */
19
+ mixed: boolean;
20
+ }
21
+ /**
22
+ * Read a dot-separated path, e.g. `"data.label"`.
23
+ *
24
+ * Returns `undefined` for a missing path rather than throwing, because a
25
+ * property panel routinely renders objects that don't have every field yet.
26
+ */
27
+ export declare const getPath: (subject: unknown, path: string) => unknown;
28
+ /**
29
+ * Return a copy of `subject` with `path` set to `value`.
30
+ *
31
+ * Every object along the path is cloned, so the original is untouched and React
32
+ * sees a new reference at each level it needs to. Missing intermediate objects
33
+ * are created.
34
+ */
35
+ export declare const setPath: <T>(subject: T, path: string, value: unknown) => T;
36
+ /** How a property is read from and written to a subject. */
37
+ export interface IPropertyAccessor<S = unknown, V = unknown> {
38
+ /** Dot path, used for both reading and writing unless overridden. */
39
+ key: string;
40
+ /** Custom read. Defaults to `getPath(subject, key)`. */
41
+ get?: (subject: S) => V;
42
+ /** Custom write, returning a new subject. Defaults to `setPath`. */
43
+ set?: (subject: S, value: V) => S;
44
+ }
45
+ /** Read a property from one subject, honouring a custom accessor. */
46
+ export declare const readValue: <S, V>(subject: S, accessor: IPropertyAccessor<S, V>) => V;
47
+ /** Write a property to one subject, honouring a custom accessor. */
48
+ export declare const writeValue: <S, V>(subject: S, accessor: IPropertyAccessor<S, V>, value: V) => S;
49
+ /**
50
+ * Read a property across a selection.
51
+ *
52
+ * With one subject this is just its value. With several, it is the shared value
53
+ * — or `mixed`, if they disagree. With none, it is empty and not mixed.
54
+ *
55
+ * Comparison is by `Object.is`, so two structurally equal objects at the same
56
+ * path read as mixed. That is deliberate: a panel cannot know whether deep
57
+ * equality is meaningful for an arbitrary payload, and claiming agreement it
58
+ * hasn't verified is the more damaging error.
59
+ */
60
+ export declare const readValues: <S, V>(subjects: S[], accessor: IPropertyAccessor<S, V>) => IPropertyValue<V>;
61
+ /**
62
+ * Apply a value to every subject, returning new copies.
63
+ *
64
+ * Subjects that already hold the value are returned unchanged by identity, so
65
+ * an edit across a large selection only invalidates what actually moved.
66
+ */
67
+ export declare const writeValues: <S, V>(subjects: S[], accessor: IPropertyAccessor<S, V>, value: V) => S[];