nexus-shell 0.1.23 → 0.2.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/README.md +191 -106
- package/dist/components/layout/ShellLayout.d.ts +90 -12
- package/dist/components/widgets/ActivityBar.d.ts +48 -1
- package/dist/components/widgets/AppTitle.d.ts +30 -0
- package/dist/components/widgets/ChatPane.d.ts +63 -1
- package/dist/components/widgets/CommandPalette.d.ts +55 -7
- package/dist/components/widgets/ContextMenu.d.ts +34 -3
- package/dist/components/widgets/DataGrid.d.ts +44 -0
- package/dist/components/widgets/MenuBar.d.ts +55 -5
- package/dist/components/widgets/Modal.d.ts +58 -0
- package/dist/components/widgets/QuickSearch.d.ts +66 -0
- package/dist/components/widgets/SearchWidget.d.ts +36 -0
- package/dist/components/widgets/SettingsPanel.d.ts +33 -0
- package/dist/components/widgets/SidebarPane.d.ts +37 -1
- package/dist/components/widgets/StatusBar.d.ts +51 -1
- package/dist/components/widgets/TerminalPane.d.ts +43 -1
- package/dist/components/widgets/ThemeSwitcher.d.ts +35 -5
- package/dist/components/widgets/TreeWidget.d.ts +94 -5
- package/dist/components/widgets/UserProfile.d.ts +75 -6
- package/dist/connected/ConnectedActivityBar.d.ts +10 -0
- package/dist/connected/ConnectedChatPane.d.ts +18 -0
- package/dist/connected/ConnectedCommandPalette.d.ts +18 -0
- package/dist/connected/ConnectedMenuBar.d.ts +12 -0
- package/dist/connected/ConnectedModal.d.ts +15 -0
- package/dist/connected/ConnectedSidebarPane.d.ts +12 -0
- package/dist/connected/ConnectedStatusBar.d.ts +15 -0
- package/dist/connected/ConnectedTerminalPane.d.ts +18 -0
- package/dist/connected/ConnectedThemeSwitcher.d.ts +11 -0
- package/dist/connected/ConnectedUserProfile.d.ts +14 -0
- package/dist/connected/index.d.ts +21 -0
- package/dist/core/Boot.d.ts +64 -2
- package/dist/core/registry/MenuRegistry.d.ts +22 -9
- package/dist/core/services/ChatService.d.ts +23 -7
- package/dist/core/services/StatusBarService.d.ts +17 -14
- package/dist/index.d.ts +53 -33
- package/dist/lib/cn.d.ts +12 -0
- package/dist/nexus-shell.es.js +2427 -40
- package/dist/nexus-shell.umd.js +1 -3707
- package/dist/style.css +1 -1
- package/package.json +43 -15
- package/dist/App.d.ts +0 -2
- package/dist/components/layout/NexusWorkspaceShell.d.ts +0 -9
- package/dist/components/widgets/AgentManager.d.ts +0 -18
- package/dist/components/widgets/ArgumentInspectorWidget.d.ts +0 -5
- package/dist/components/widgets/DialogueMapperInspector.d.ts +0 -5
- package/dist/components/widgets/DialogueMapperLibrary.d.ts +0 -11
- package/dist/components/widgets/DialogueMapperLibraryWidget.d.ts +0 -3
- package/dist/components/widgets/DialogueMappingWidget.d.ts +0 -10
- package/dist/components/widgets/FilesSidebar.d.ts +0 -1
- package/dist/components/widgets/FlowControlToolbar.d.ts +0 -18
- package/dist/components/widgets/GlobalModal.d.ts +0 -3
- package/dist/components/widgets/MenuBarSearch.d.ts +0 -11
- package/dist/components/widgets/MockupReviewWidget.d.ts +0 -3
- package/dist/components/widgets/NexusWorkspaceTitle.d.ts +0 -10
- package/dist/components/widgets/ProjectPropertiesWidget.d.ts +0 -8
- package/dist/components/widgets/WargameMap.d.ts +0 -31
- package/dist/components/widgets/WelcomeTab.d.ts +0 -8
- package/dist/components/widgets/dialogue-mapper/IbisNode.d.ts +0 -5
- package/dist/components/widgets/mockup-reviewer/ExecutionOverlay.d.ts +0 -10
- package/dist/components/widgets/mockup-reviewer/HtmlShadowRenderer.d.ts +0 -9
- package/dist/components/widgets/mockup-reviewer/ImplementationPlanWorkspace.d.ts +0 -8
- package/dist/components/widgets/mockup-reviewer/MockupReviewWorkspace.d.ts +0 -10
- package/dist/components/widgets/mockup-reviewer/ReviewSidebar.d.ts +0 -3
- package/dist/components/widgets/mockup-reviewer/ReviewToolbar.d.ts +0 -12
- package/dist/components/widgets/mockup-reviewer/WorkflowMapper.d.ts +0 -7
- package/dist/components/widgets/wiki/AiCoWriterWorkspace.d.ts +0 -3
- package/dist/components/widgets/wiki/SemanticLinkMapper.d.ts +0 -3
- package/dist/components/widgets/wiki/WikiChatWidget.d.ts +0 -3
- package/dist/components/widgets/wiki/WikiEditor.d.ts +0 -3
- package/dist/components/widgets/wiki/WikiHistorySidebar.d.ts +0 -3
- package/dist/components/widgets/wiki/WikiNavigator.d.ts +0 -3
- package/dist/core/services/DialogueMappingService.d.ts +0 -59
- package/dist/core/services/FileStoreService.d.ts +0 -11
- package/dist/core/services/MockupReviewService.d.ts +0 -58
- package/dist/core/services/dialogue-mapper/DialogueLayoutEngine.d.ts +0 -9
- package/dist/core/services/dialogue-mapper/DialogueMapRepository.d.ts +0 -15
- package/dist/core/services/mockup-reviewer/MockupFixtures.d.ts +0 -11
- package/dist/core/services/useWikiStore.d.ts +0 -33
- package/dist/index-4EyxMz0x.js +0 -78516
- package/dist/main.d.ts +0 -1
- package/dist/maplibre-gl-DJOCpYhv.js +0 -24588
- package/dist/plugins/WikiPlugin.d.ts +0 -3
- package/dist/stories/ActivityBar.stories.d.ts +0 -9
- package/dist/stories/AgentManager.stories.d.ts +0 -25
- package/dist/stories/ChatPane.stories.d.ts +0 -10
- package/dist/stories/CommandPalette.stories.d.ts +0 -9
- package/dist/stories/DataGrid.stories.d.ts +0 -12
- package/dist/stories/DialogueMapperLibrary.stories.d.ts +0 -10
- package/dist/stories/DialogueMappingWidget.stories.d.ts +0 -9
- package/dist/stories/FlowControlToolbar.stories.d.ts +0 -14
- package/dist/stories/GlobalModal.stories.d.ts +0 -7
- package/dist/stories/IbisNode.stories.d.ts +0 -10
- package/dist/stories/MenuBar.stories.d.ts +0 -10
- package/dist/stories/MenuBarSearch.stories.d.ts +0 -7
- package/dist/stories/MockupReviewWidget.stories.d.ts +0 -9
- package/dist/stories/MockupReviewerLayout.stories.d.ts +0 -8
- package/dist/stories/NexusWorkspaceShell.stories.d.ts +0 -9
- package/dist/stories/NexusWorkspaceTitle.stories.d.ts +0 -11
- package/dist/stories/SearchWidget.stories.d.ts +0 -9
- package/dist/stories/ShellLayout.stories.d.ts +0 -13
- package/dist/stories/SidebarPane.stories.d.ts +0 -8
- package/dist/stories/StatusBar.stories.d.ts +0 -10
- package/dist/stories/TerminalPane.stories.d.ts +0 -9
- package/dist/stories/ThemeSwitcher.stories.d.ts +0 -10
- package/dist/stories/TreeWidget.stories.d.ts +0 -8
- package/dist/stories/UserProfile.stories.d.ts +0 -11
- package/dist/stories/WargameMap.stories.d.ts +0 -14
- package/dist/stories/wiki/WikiComposition.stories.d.ts +0 -6
- package/dist/stories/wiki/WikiWidgets.stories.d.ts +0 -11
package/README.md
CHANGED
|
@@ -1,147 +1,232 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Nexus Shell
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://github.com/techmuch/nexus-shell/actions/workflows/deploy-storybook.yml)
|
|
6
|
-
[](https://techmuch.github.io/nexus-shell/?path=/docs/introduction--docs)
|
|
3
|
+
React components for building IDE-style application shells — menu bar, activity
|
|
4
|
+
bar, sidebar, docking layout, terminal, chat pane and status bar.
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
**[Documentation and live examples →](https://techmuch.github.io/nexus-shell/)** ·
|
|
7
|
+
[Storybook](https://techmuch.github.io/nexus-shell/storybook/)
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
```bash
|
|
10
|
+
npm install nexus-shell
|
|
11
|
+
```
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
```ts
|
|
14
|
+
import 'nexus-shell/style.css';
|
|
15
|
+
```
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
Explore the live documentation, interactive component playground, prop definitions, and layout compositions at:
|
|
16
|
-
👉 **[Live Storybook Documentation Site](https://techmuch.github.io/nexus-shell/?path=/docs/introduction--docs)**
|
|
17
|
+
React 19 is a peer dependency.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
## Start with the shell
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
An application built on this library starts with `ShellLayout` and grows by
|
|
22
|
+
registering features into it.
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
```tsx
|
|
25
|
+
// main.tsx — a complete application
|
|
26
|
+
import { createRoot } from 'react-dom/client';
|
|
27
|
+
import { Boxes, Files } from 'lucide-react';
|
|
28
|
+
import {
|
|
29
|
+
AppTitle,
|
|
30
|
+
ShellLayout,
|
|
31
|
+
componentRegistry,
|
|
32
|
+
initializeShell,
|
|
33
|
+
useLayoutStore,
|
|
34
|
+
} from 'nexus-shell';
|
|
35
|
+
import 'nexus-shell/style.css';
|
|
36
|
+
|
|
37
|
+
// Views register by id, so the shell never has to import them.
|
|
38
|
+
componentRegistry.register('editor', Editor);
|
|
39
|
+
|
|
40
|
+
initializeShell({
|
|
41
|
+
panels: [{ id: 'files', label: 'Explorer', icon: Files, component: FileExplorer }],
|
|
42
|
+
commands: [
|
|
43
|
+
{ id: 'file.save', label: 'File: Save', keybinding: 'Control+s', execute: save },
|
|
44
|
+
],
|
|
45
|
+
menus: { File: [{ id: 'save', label: 'Save', commandId: 'file.save' }] },
|
|
46
|
+
statusBar: [{ id: 'branch', label: 'main', alignment: 'left' }],
|
|
47
|
+
});
|
|
26
48
|
|
|
27
|
-
|
|
28
|
-
Fully responsive styling driven by HSL CSS variables, conforming to WCAG contrast standards.
|
|
29
|
-

|
|
30
|
-
*(Above: Light Theme workspace showing responsive menus, active tab headers, and the account widget)*
|
|
49
|
+
useLayoutStore.getState().addTab('editor', 'App.tsx');
|
|
31
50
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
createRoot(document.getElementById('root')!).render(
|
|
52
|
+
<ShellLayout title={<AppTitle title="Acme Studio" icon={<Boxes size={16} />} />} />,
|
|
53
|
+
);
|
|
54
|
+
```
|
|
36
55
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-

|
|
56
|
+
That is a working IDE: dockable tabs, a command palette on `Cmd/Ctrl+Shift+P`, a
|
|
57
|
+
terminal, a chat pane, and a themable frame.
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
## Growing an app
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
You add capability by **registering** it. None of this changes the layout, which
|
|
62
|
+
is what lets plugins and distant code contribute to the shell.
|
|
44
63
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
64
|
+
| To add | Do this |
|
|
65
|
+
| :-- | :-- |
|
|
66
|
+
| A view or tab | `componentRegistry.register(id, Component)`, then `useLayoutStore().addTab(id, title)` |
|
|
67
|
+
| An action or hotkey | `commandRegistry.registerCommand({ id, label, keybinding, execute })` — appears in the palette automatically |
|
|
68
|
+
| A menu entry | `menuRegistry.registerMenu('File', { id, label, commandId })` |
|
|
69
|
+
| A sidebar panel | Add it to `panels`; it becomes an activity bar icon |
|
|
70
|
+
| A status bar item | `useStatusBarStore.getState().addWidget(...)` |
|
|
71
|
+
| A dialog from anywhere | `await useModalStore.getState().openConfirm('Sure?')` |
|
|
72
|
+
| An unsaved-changes guard | `useLayoutStore.getState().setTabDirty(tabId, true)` |
|
|
51
73
|
|
|
52
|
-
|
|
74
|
+
## When you need less than the shell
|
|
53
75
|
|
|
54
|
-
|
|
76
|
+
Every component the shell is assembled from is exported on its own — pure and
|
|
77
|
+
prop-driven, with no global state or registry lookups. This is the escape hatch:
|
|
78
|
+
reach for it when you're embedding one piece into an app you already have, or
|
|
79
|
+
building a frame `ShellLayout` can't express.
|
|
55
80
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
81
|
+
```tsx
|
|
82
|
+
import { ActivityBar, SidebarPane, StatusBar, TreeWidget } from 'nexus-shell';
|
|
83
|
+
|
|
84
|
+
const [active, setActive] = useState<string | null>('files');
|
|
85
|
+
|
|
86
|
+
<div className="theme-dark flex h-screen flex-col">
|
|
87
|
+
<div className="flex flex-1">
|
|
88
|
+
<ActivityBar
|
|
89
|
+
items={panels}
|
|
90
|
+
activeId={active}
|
|
91
|
+
onSelect={(id) => setActive(id === active ? null : id)}
|
|
92
|
+
/>
|
|
93
|
+
{active && (
|
|
94
|
+
<SidebarPane title="Explorer" onClose={() => setActive(null)}>
|
|
95
|
+
<TreeWidget data={files} onToggle={toggle} onActivate={open} />
|
|
96
|
+
</SidebarPane>
|
|
97
|
+
)}
|
|
98
|
+
<main className="flex-1">{children}</main>
|
|
99
|
+
</div>
|
|
100
|
+
<StatusBar widgets={[{ id: 'branch', label: 'main', alignment: 'left' }]} />
|
|
101
|
+
</div>;
|
|
102
|
+
```
|
|
61
103
|
|
|
62
|
-
|
|
104
|
+
These are controlled, so state stays in your app — a real cost the shell was
|
|
105
|
+
absorbing for you.
|
|
63
106
|
|
|
64
|
-
|
|
107
|
+
Between the two sit the `Connected*` variants, each binding one component to its
|
|
108
|
+
store. `ShellLayout` is built entirely out of them, so you can rearrange the
|
|
109
|
+
shell's own frame and keep every registration working.
|
|
65
110
|
|
|
66
|
-
|
|
111
|
+
## Components
|
|
67
112
|
|
|
68
|
-
|
|
69
|
-
npm install nexus-shell
|
|
70
|
-
```
|
|
113
|
+
`ShellLayout` assembles all of these. Each is also exported on its own.
|
|
71
114
|
|
|
72
|
-
|
|
115
|
+
| Component | What it is |
|
|
116
|
+
| :-- | :-- |
|
|
117
|
+
| `ActivityBar` | Vertical icon rail for switching sidebar panels |
|
|
118
|
+
| `ChatPane` | Docked chat transcript with slash-command autocomplete |
|
|
119
|
+
| `CommandPalette` | Filterable, keyboard-navigable command list |
|
|
120
|
+
| `ContextMenu` | Floating menu positioned at viewport coordinates |
|
|
121
|
+
| `DataGrid` | Sortable, filterable table with optional virtualisation |
|
|
122
|
+
| `MenuBar` | Application menu bar with submenus and content slots |
|
|
123
|
+
| `Modal` | Alert / confirm / prompt dialog |
|
|
124
|
+
| `QuickSearch` | Compact search field with grouped results, for the menu bar |
|
|
125
|
+
| `SearchWidget` | Full-height search panel with autocomplete, for the sidebar |
|
|
126
|
+
| `SettingsPanel` | Theme picker body for the settings sidebar |
|
|
127
|
+
| `SidebarPane` | Titled, closable container for sidebar content |
|
|
128
|
+
| `StatusBar` | Footer with left / center / right item groups |
|
|
129
|
+
| `TerminalPane` | Bottom-docked terminal log and input |
|
|
130
|
+
| `ThemeSwitcher` | Segmented theme control |
|
|
131
|
+
| `TreeWidget` | Virtualised file tree with drag-to-move and a data-driven menu |
|
|
132
|
+
| `UserProfile` | Avatar and identity widget |
|
|
73
133
|
|
|
74
|
-
|
|
134
|
+
## Theming
|
|
75
135
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
import { ShellLayout, initializeShell } from 'nexus-shell';
|
|
80
|
-
import 'nexus-shell/style.css'; // Essential styling properties
|
|
81
|
-
|
|
82
|
-
// Initialize core registries and default welcome workspace
|
|
83
|
-
initializeShell();
|
|
84
|
-
|
|
85
|
-
export default function App() {
|
|
86
|
-
return (
|
|
87
|
-
<div className="h-screen w-screen bg-background text-foreground">
|
|
88
|
-
<ShellLayout
|
|
89
|
-
title={<div className="font-bold text-lg">My Custom IDE</div>}
|
|
90
|
-
/>
|
|
91
|
-
</div>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
|
|
96
|
-
```
|
|
136
|
+
Three themes ship in the stylesheet: `theme-light`, `theme-dark`, `theme-gt`.
|
|
137
|
+
Colors are CSS custom properties scoped to those classes, so switching themes is
|
|
138
|
+
a class on an ancestor — no React context, no provider.
|
|
97
139
|
|
|
98
|
-
|
|
140
|
+
Add your own by defining a `.theme-yourname` block with the same properties.
|
|
99
141
|
|
|
100
|
-
##
|
|
142
|
+
## Development
|
|
101
143
|
|
|
102
|
-
|
|
144
|
+
```bash
|
|
145
|
+
npm install
|
|
146
|
+
|
|
147
|
+
npm run site # documentation site at :5173
|
|
148
|
+
npm run storybook # component workbench at :6006
|
|
149
|
+
npm run dev # showcase app at :5173
|
|
150
|
+
|
|
151
|
+
npm run typecheck # library sources
|
|
152
|
+
npm test # vitest — unit and architecture tests
|
|
153
|
+
npm run site:test # renders every site route and demo
|
|
154
|
+
npm run build # library build to dist/
|
|
155
|
+
npm run build-storybook
|
|
156
|
+
npm run site:build # documentation site to website/dist/
|
|
157
|
+
npm run e2e # Playwright against the showcase app
|
|
158
|
+
```
|
|
103
159
|
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
import { commandRegistry } from 'nexus-shell';
|
|
160
|
+
`npm run typecheck` covers `src/` only. To include the examples:
|
|
107
161
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
label: 'Say Hello',
|
|
111
|
-
keybinding: 'Control+L',
|
|
112
|
-
execute: () => alert('Hello from Nexus Shell!'),
|
|
113
|
-
});
|
|
162
|
+
```bash
|
|
163
|
+
npx tsc --noEmit -p tsconfig.examples.json
|
|
114
164
|
```
|
|
115
165
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
166
|
+
CI runs all of the above on every push and pull request.
|
|
167
|
+
|
|
168
|
+
### Layout
|
|
119
169
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
170
|
+
```
|
|
171
|
+
src/
|
|
172
|
+
components/ Pure, prop-driven components — what the shell is made of
|
|
173
|
+
connected/ Store-bound wrappers used by ShellLayout
|
|
174
|
+
core/ Stores (zustand), registries, and initializeShell
|
|
175
|
+
lib/ Shared utilities
|
|
176
|
+
stories/ MDX documentation pages
|
|
177
|
+
examples/
|
|
178
|
+
showcase/ Demo apps built on the library — not published
|
|
179
|
+
dialogue-mapper/
|
|
180
|
+
mockup-reviewer/
|
|
181
|
+
wiki/
|
|
182
|
+
wargame/
|
|
183
|
+
e2e/ Playwright specs for the showcase app
|
|
184
|
+
basic-app/ Minimal integration example
|
|
185
|
+
website/
|
|
186
|
+
src/demos/ Live examples — the source shown on the site
|
|
187
|
+
src/content/ The component gallery, as data
|
|
188
|
+
scripts/ Generates the props tables from the library's types
|
|
126
189
|
```
|
|
127
190
|
|
|
128
|
-
|
|
191
|
+
### Documentation site
|
|
129
192
|
|
|
130
|
-
|
|
193
|
+
`website/` is a Vite app that imports the library from `../src`, not from
|
|
194
|
+
`dist`. Every example on the site is rendered by the current source, so a demo
|
|
195
|
+
that no longer compiles against the real API fails the build rather than
|
|
196
|
+
quietly documenting an API that no longer exists.
|
|
131
197
|
|
|
132
|
-
|
|
133
|
-
# Start Vite App & local Storybook server
|
|
134
|
-
npm run dev:all
|
|
198
|
+
Two generation steps keep it honest:
|
|
135
199
|
|
|
136
|
-
|
|
137
|
-
|
|
200
|
+
- **Props tables** come from `website/scripts/generate-api.mjs`, which parses
|
|
201
|
+
the component sources for prop names, types, defaults and JSDoc. Nothing is
|
|
202
|
+
hand-written.
|
|
203
|
+
- **Code samples** are extracted from the demo files by `#region` marker, so the
|
|
204
|
+
snippet a visitor copies is provably the code that produced the thing they
|
|
205
|
+
just interacted with.
|
|
138
206
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
207
|
+
Adding a component to the site is one entry in `website/src/content/components.ts`
|
|
208
|
+
plus a demo file. There is no per-component page to keep in sync.
|
|
209
|
+
|
|
210
|
+
The site ships no dependencies of its own — routing, syntax highlighting and the
|
|
211
|
+
copy button are all local, so the docs can't drag the repo's install surface
|
|
212
|
+
around.
|
|
213
|
+
|
|
214
|
+
**Deployment.** `.github/workflows/deploy-pages.yml` builds the site and
|
|
215
|
+
Storybook on every push to `main` and publishes them as one Pages artifact — the
|
|
216
|
+
site at the root, Storybook under `/storybook/`. First-time setup needs your
|
|
217
|
+
credentials: run `./scripts/setup-pages.sh`.
|
|
218
|
+
|
|
219
|
+
The split between `components/` and `connected/` is the load-bearing one. A
|
|
220
|
+
component that reads a global store can't be configured by its caller, rendered
|
|
221
|
+
twice with different data, or documented — its props table is empty because it
|
|
222
|
+
has no props. Everything in `components/` is therefore forbidden from importing
|
|
223
|
+
`core/services`, and the tests in `components/__tests__` exist to keep it that
|
|
224
|
+
way.
|
|
142
225
|
|
|
143
|
-
|
|
226
|
+
Anything under `examples/` is a consumer of the library, not part of it. It's
|
|
227
|
+
built and documented alongside the components to prove the primitives are
|
|
228
|
+
sufficient, but it ships to nobody.
|
|
144
229
|
|
|
145
|
-
##
|
|
230
|
+
## License
|
|
146
231
|
|
|
147
|
-
MIT
|
|
232
|
+
MIT
|
|
@@ -1,17 +1,95 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Model, IJsonModel } from 'flexlayout-react';
|
|
3
|
+
import { IMenuItemConfig } from '../../core/registry/MenuRegistry';
|
|
4
|
+
import { ISlashCommandConfig } from '../../core/services/ChatService';
|
|
2
5
|
import { ISidebarPanel } from '../../core/services/SidebarService';
|
|
3
|
-
import {
|
|
4
|
-
import { IStatusBarWidget } from '../../core/services/StatusBarService';
|
|
5
|
-
import { IMenuItem } from '../../core/registry/MenuRegistry';
|
|
6
|
+
import { IStatusBarWidgetConfig } from '../../core/services/StatusBarService';
|
|
6
7
|
|
|
7
|
-
interface ShellLayoutProps {
|
|
8
|
+
export interface ShellLayoutProps {
|
|
9
|
+
/**
|
|
10
|
+
* Sidebar panels to register. Each becomes an icon in the activity bar and
|
|
11
|
+
* renders its `component` in the sidebar when selected.
|
|
12
|
+
*/
|
|
8
13
|
panels?: ISidebarPanel[];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
/** Slash commands available in the chat pane. */
|
|
15
|
+
slashCommands?: ISlashCommandConfig[];
|
|
16
|
+
/** Menu structure, keyed by top-level menu name. Items dispatch by `commandId`. */
|
|
17
|
+
menuConfig?: Record<string, IMenuItemConfig[]>;
|
|
18
|
+
/** Items for the status bar, grouped by their `alignment`. */
|
|
19
|
+
statusBarConfig?: IStatusBarWidgetConfig[];
|
|
20
|
+
/** Branding shown at the far left of the menu bar. */
|
|
21
|
+
title?: ReactNode;
|
|
22
|
+
/** Slot at the far right of the menu bar, for actions or a theme switcher. */
|
|
23
|
+
rightMenuBarContent?: ReactNode;
|
|
24
|
+
/** Widget rendered between the menus and the right slot, sized for search. */
|
|
25
|
+
centerMenuBarContent?: ReactNode;
|
|
26
|
+
/**
|
|
27
|
+
* Take control of the docking layout. When provided, the shell renders this
|
|
28
|
+
* model and stops writing changes to `useLayoutStore` — useful for tests and
|
|
29
|
+
* stories. Omit it to use the store's persisted model.
|
|
30
|
+
*/
|
|
14
31
|
layoutModel?: Model;
|
|
32
|
+
/**
|
|
33
|
+
* Starting workspace, as a `flexlayout-react` JSON model. Applied once on
|
|
34
|
+
* mount and only when nothing has been restored from storage, so it seeds a
|
|
35
|
+
* first run without overwriting a layout the user has since arranged.
|
|
36
|
+
*
|
|
37
|
+
* Ignored when `layoutModel` is provided, which takes control outright.
|
|
38
|
+
*/
|
|
39
|
+
initialLayoutJson?: IJsonModel;
|
|
40
|
+
/**
|
|
41
|
+
* Stop persisting the layout to `localStorage`. The workspace then resets to
|
|
42
|
+
* `initialLayoutJson` on every load — useful for embedded or multi-tenant
|
|
43
|
+
* shells where a per-browser layout would be wrong. Defaults to `false`.
|
|
44
|
+
*/
|
|
45
|
+
disableLocalStorage?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Called with the serialised model whenever the user moves, splits, opens or
|
|
48
|
+
* closes a tab. Use it to persist the layout somewhere of your own — a user
|
|
49
|
+
* profile on the server, say — instead of, or alongside, `localStorage`.
|
|
50
|
+
*/
|
|
51
|
+
onLayoutChange?: (layout: IJsonModel) => void;
|
|
15
52
|
}
|
|
16
|
-
|
|
17
|
-
|
|
53
|
+
/**
|
|
54
|
+
* The complete application shell: menu bar, activity bar, sidebar, a
|
|
55
|
+
* `flexlayout-react` docking workspace, terminal, chat pane and status bar.
|
|
56
|
+
*
|
|
57
|
+
* **This is where an application starts.** Call {@link initializeShell} once to
|
|
58
|
+
* register your commands, menus and panels, render this component, and add
|
|
59
|
+
* features by registering them rather than by restructuring the layout. Tab
|
|
60
|
+
* contents resolve through the {@link componentRegistry}, so a new view is a
|
|
61
|
+
* registration and a menu entry — the shell never needs to import it.
|
|
62
|
+
*
|
|
63
|
+
* Configuration can arrive either way. Props are convenient for values that
|
|
64
|
+
* change with your app's state; {@link initializeShell} is better for the fixed
|
|
65
|
+
* set, since it runs once and works outside React. Props passed here are
|
|
66
|
+
* written into the same stores on mount, so the two are interchangeable.
|
|
67
|
+
*
|
|
68
|
+
* Internally this composes the `Connected*` component variants, which read from
|
|
69
|
+
* the shell stores. Those components — and the pure ones beneath them — are
|
|
70
|
+
* exported too, for the cases where you need to rearrange the frame itself.
|
|
71
|
+
*
|
|
72
|
+
* Closing a tab marked dirty via `useLayoutStore().setTabDirty` prompts for
|
|
73
|
+
* confirmation before the tab is removed.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* // main.tsx — a complete application
|
|
78
|
+
* import { initializeShell, ShellLayout, componentRegistry, AppTitle } from 'nexus-shell';
|
|
79
|
+
* import 'nexus-shell/style.css';
|
|
80
|
+
*
|
|
81
|
+
* componentRegistry.register('editor', Editor);
|
|
82
|
+
*
|
|
83
|
+
* initializeShell({
|
|
84
|
+
* panels: [{ id: 'files', label: 'Explorer', icon: Files, component: FileTree }],
|
|
85
|
+
* commands: [{ id: 'file.save', label: 'File: Save', keybinding: 'Control+s', execute: save }],
|
|
86
|
+
* menus: { File: [{ id: 'save', label: 'Save', commandId: 'file.save' }] },
|
|
87
|
+
* statusBar: [{ id: 'branch', label: 'main', alignment: 'left' }],
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* createRoot(document.getElementById('root')!).render(
|
|
91
|
+
* <ShellLayout title={<AppTitle title="Acme Studio" icon={<Boxes size={16} />} />} />,
|
|
92
|
+
* );
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare const ShellLayout: ({ panels, slashCommands, menuConfig, statusBarConfig, title, rightMenuBarContent, centerMenuBarContent, layoutModel, initialLayoutJson, disableLocalStorage, onLayoutChange, }: ShellLayoutProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
import { LucideIcon } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
/** A single icon button in the {@link ActivityBar}. */
|
|
4
|
+
export interface IActivityBarItem {
|
|
5
|
+
/** Stable identifier reported to `onSelect`. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Accessible label and tooltip text. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** `lucide-react` icon rendered at 20px. */
|
|
10
|
+
icon: LucideIcon;
|
|
11
|
+
}
|
|
12
|
+
export interface ActivityBarProps {
|
|
13
|
+
/** Items rendered in the main (top) group, in order. */
|
|
14
|
+
items?: IActivityBarItem[];
|
|
15
|
+
/**
|
|
16
|
+
* Items rendered in the bottom group, below the flex spacer. Defaults to a
|
|
17
|
+
* single Settings item. Pass `[]` to render no bottom group.
|
|
18
|
+
*/
|
|
19
|
+
bottomItems?: IActivityBarItem[];
|
|
20
|
+
/** Id of the currently active item, or `null` when nothing is selected. */
|
|
21
|
+
activeId?: string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Called with the clicked item's id. Toggling — clicking the active item to
|
|
24
|
+
* deselect it — is the caller's decision, not the component's.
|
|
25
|
+
*/
|
|
26
|
+
onSelect?: (id: string) => void;
|
|
27
|
+
/** Extra classes merged onto the root `<aside>`. */
|
|
28
|
+
className?: string;
|
|
29
|
+
/** Accessible label for the landmark. Defaults to `"Activity Bar"`. */
|
|
30
|
+
'aria-label'?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The narrow vertical icon rail on the left edge of the shell, used to switch
|
|
34
|
+
* which panel the sidebar shows.
|
|
35
|
+
*
|
|
36
|
+
* Controlled — it holds no selection state. For the store-backed variant used
|
|
37
|
+
* by `ShellLayout`, see `ConnectedActivityBar`.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* <ActivityBar
|
|
42
|
+
* items={[{ id: 'files', label: 'Explorer', icon: Files }]}
|
|
43
|
+
* activeId={active}
|
|
44
|
+
* onSelect={(id) => setActive(id === active ? null : id)}
|
|
45
|
+
* />
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare const ActivityBar: ({ items, bottomItems, activeId, onSelect, className, "aria-label": ariaLabel, }: ActivityBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface AppTitleProps {
|
|
4
|
+
/** Primary line. Rendered uppercase in a mono face. */
|
|
5
|
+
title: string;
|
|
6
|
+
/** Secondary line beneath the title. Omit to render a single line. */
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Element shown in the leading badge, typically a 16px `lucide-react` icon.
|
|
10
|
+
* Omit to render no badge at all.
|
|
11
|
+
*/
|
|
12
|
+
icon?: React.ReactNode;
|
|
13
|
+
/** Extra classes merged onto the root element. */
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A branding lockup — icon badge, title and subtitle — sized for the
|
|
18
|
+
* {@link MenuBar}'s `title` slot.
|
|
19
|
+
*
|
|
20
|
+
* Purely presentational. Pass whatever branding your app needs; the library
|
|
21
|
+
* supplies no default copy.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <MenuBar
|
|
26
|
+
* title={<AppTitle title="Acme Studio" subtitle="Design System" icon={<Boxes size={16} />} />}
|
|
27
|
+
* />
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare const AppTitle: ({ title, subtitle, icon, className }: AppTitleProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
/** A single message in the {@link ChatPane} transcript. */
|
|
4
|
+
export interface IChatMessage {
|
|
5
|
+
/** Stable identifier. Used as the React key. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Who sent it. `'user'` right-aligns; anything else left-aligns. */
|
|
8
|
+
role: 'user' | 'assistant';
|
|
9
|
+
/** Message body. Rendered as plain text with newlines preserved. */
|
|
10
|
+
text: string;
|
|
11
|
+
/** Optional display name shown above the bubble. Defaults from `role`. */
|
|
12
|
+
author?: string;
|
|
13
|
+
}
|
|
14
|
+
/** A `/`-prefixed shortcut offered in the composer's autocomplete. */
|
|
15
|
+
export interface ISlashCommand {
|
|
16
|
+
/** Command word without the leading slash, e.g. `"clear"`. */
|
|
17
|
+
command: string;
|
|
18
|
+
/** One-line description shown beside the command in the suggestion list. */
|
|
19
|
+
description: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ChatPaneProps {
|
|
22
|
+
/** Messages to display, oldest first. */
|
|
23
|
+
messages?: IChatMessage[];
|
|
24
|
+
/**
|
|
25
|
+
* Called with the trimmed message text on send. The component clears its
|
|
26
|
+
* composer but does not append to `messages` — that is the caller's job.
|
|
27
|
+
*/
|
|
28
|
+
onSend?: (text: string) => void;
|
|
29
|
+
/** Slash commands offered once the composer starts with `/`. */
|
|
30
|
+
slashCommands?: ISlashCommand[];
|
|
31
|
+
/** Called when a slash command is picked from the suggestion list. */
|
|
32
|
+
onSlashCommand?: (command: ISlashCommand) => void;
|
|
33
|
+
/** Called when the close button is pressed. Omit to hide the button. */
|
|
34
|
+
onClose?: () => void;
|
|
35
|
+
/** Title shown in the pane header. Defaults to `"Chat"`. */
|
|
36
|
+
title?: string;
|
|
37
|
+
/** Placeholder for the composer. */
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
/** Shown in place of the transcript when `messages` is empty. */
|
|
40
|
+
emptyState?: React.ReactNode;
|
|
41
|
+
/** Extra classes merged onto the root element. */
|
|
42
|
+
className?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A docked chat panel: a scrolling transcript over a composer with slash-command
|
|
46
|
+
* autocomplete.
|
|
47
|
+
*
|
|
48
|
+
* Fully controlled — it owns only the in-progress input and the suggestion
|
|
49
|
+
* highlight. Messages, sending and visibility belong to the caller, so the same
|
|
50
|
+
* component works against a local array, a websocket, or an LLM endpoint. For
|
|
51
|
+
* the store-backed variant used by `ShellLayout`, see `ConnectedChatPane`.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* <ChatPane
|
|
56
|
+
* messages={messages}
|
|
57
|
+
* onSend={(text) => send(text)}
|
|
58
|
+
* slashCommands={[{ command: 'clear', description: 'Clear the transcript' }]}
|
|
59
|
+
* onClose={() => setOpen(false)}
|
|
60
|
+
* />
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare const ChatPane: ({ messages, onSend, slashCommands, onSlashCommand, onClose, title, placeholder, emptyState, className, }: ChatPaneProps) => import("react/jsx-runtime").JSX.Element;
|