nexus-shell 0.2.2 → 0.2.4
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 +4 -3
- package/dist/components/widgets/SettingsPanel.d.ts +6 -1
- package/dist/components/widgets/ThemeSwitcher.d.ts +7 -2
- package/dist/components/widgets/TreeWidget.d.ts +89 -44
- package/dist/core/Boot.d.ts +1 -1
- package/dist/core/services/ThemeService.d.ts +10 -1
- package/dist/index.d.ts +1 -0
- package/dist/lib/themes.d.ts +57 -0
- package/dist/nexus-shell.es.js +833 -823
- package/dist/nexus-shell.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ const [active, setActive] = useState<string | null>('files');
|
|
|
92
92
|
/>
|
|
93
93
|
{active && (
|
|
94
94
|
<SidebarPane title="Explorer" onClose={() => setActive(null)}>
|
|
95
|
-
<TreeWidget data={
|
|
95
|
+
<TreeWidget data={nodes} onToggle={toggle} onActivate={open} />
|
|
96
96
|
</SidebarPane>
|
|
97
97
|
)}
|
|
98
98
|
<main className="flex-1">{children}</main>
|
|
@@ -128,12 +128,13 @@ shell's own frame and keep every registration working.
|
|
|
128
128
|
| `StatusBar` | Footer with left / center / right item groups |
|
|
129
129
|
| `TerminalPane` | Bottom-docked terminal log and input |
|
|
130
130
|
| `ThemeSwitcher` | Segmented theme control |
|
|
131
|
-
| `TreeWidget` | Virtualised
|
|
131
|
+
| `TreeWidget` | Virtualised tree for any hierarchy, with drag-to-move and a data-driven menu |
|
|
132
132
|
| `UserProfile` | Avatar and identity widget |
|
|
133
133
|
|
|
134
134
|
## Theming
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
Four themes ship in the stylesheet: `theme-light`, `theme-dark`, `theme-gt`
|
|
137
|
+
(Georgia Tech) and `theme-tamu` (Texas A&M).
|
|
137
138
|
Colors are CSS custom properties scoped to those classes, so switching themes is
|
|
138
139
|
a class on an ancestor — no React context, no provider.
|
|
139
140
|
|
|
@@ -6,13 +6,18 @@ export interface ISettingsThemeOption<T extends string = string> {
|
|
|
6
6
|
label: string;
|
|
7
7
|
icon: LucideIcon;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* The themes bundled in the library stylesheet, with their full labels.
|
|
11
|
+
* Derived from the theme registry, so a new bundled theme appears here without
|
|
12
|
+
* a code change.
|
|
13
|
+
*/
|
|
9
14
|
export declare const DEFAULT_SETTINGS_THEMES: ISettingsThemeOption[];
|
|
10
15
|
export interface SettingsPanelProps<T extends string = string> {
|
|
11
16
|
/** Currently selected theme id. */
|
|
12
17
|
theme: T;
|
|
13
18
|
/** Called with the newly selected theme id. */
|
|
14
19
|
onThemeChange: (theme: T) => void;
|
|
15
|
-
/** Theme choices to offer. Defaults to
|
|
20
|
+
/** Theme choices to offer. Defaults to every bundled theme. */
|
|
16
21
|
themes?: ISettingsThemeOption<T>[];
|
|
17
22
|
/** Extra classes merged onto the root element. */
|
|
18
23
|
className?: string;
|
|
@@ -5,6 +5,11 @@ export interface IThemeOption<T extends string = string> {
|
|
|
5
5
|
/** Short label shown on the segment. Kept terse — segments are compact. */
|
|
6
6
|
label: string;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* The themes bundled in the library stylesheet, using their abbreviated labels
|
|
10
|
+
* because the segments are narrow. Derived from the theme registry, so a new
|
|
11
|
+
* bundled theme appears here without a code change.
|
|
12
|
+
*/
|
|
8
13
|
export declare const DEFAULT_THEME_OPTIONS: IThemeOption[];
|
|
9
14
|
export interface ThemeSwitcherProps<T extends string = string> {
|
|
10
15
|
/** Currently selected theme id. */
|
|
@@ -12,8 +17,8 @@ export interface ThemeSwitcherProps<T extends string = string> {
|
|
|
12
17
|
/** Called with the newly selected theme id. */
|
|
13
18
|
onChange: (theme: T) => void;
|
|
14
19
|
/**
|
|
15
|
-
* Selectable themes. Defaults to
|
|
16
|
-
*
|
|
20
|
+
* Selectable themes. Defaults to every theme bundled in the library's
|
|
21
|
+
* stylesheet. Supply your own to offer a different set.
|
|
17
22
|
*/
|
|
18
23
|
options?: IThemeOption<T>[];
|
|
19
24
|
/** Extra classes merged onto the root element. */
|
|
@@ -1,31 +1,60 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* A node in the {@link TreeWidget} hierarchy.
|
|
5
|
+
*
|
|
6
|
+
* The model is deliberately domain-neutral: the component knows about branches
|
|
7
|
+
* and leaves, and nothing else. A file explorer, an org chart, a scene graph and
|
|
8
|
+
* a category picker are all the same shape here — what differs is your `kind`
|
|
9
|
+
* and your `icon`.
|
|
10
|
+
*/
|
|
4
11
|
export interface ITreeNode {
|
|
5
12
|
/** Stable identifier, unique across the whole tree. */
|
|
6
13
|
id: string;
|
|
7
14
|
/** Text shown for the node. */
|
|
8
15
|
label: string;
|
|
9
|
-
/**
|
|
10
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Whether this node can contain children — a folder, a department, a group.
|
|
18
|
+
* Branches expand on click and accept drops; leaves do neither.
|
|
19
|
+
*
|
|
20
|
+
* Defaults to `false`. Set it explicitly for a branch whose children haven't
|
|
21
|
+
* been loaded yet, so it still renders as expandable.
|
|
22
|
+
*/
|
|
23
|
+
isBranch?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Your own classification for this node, e.g. `"folder"`, `"department"`,
|
|
26
|
+
* `"question"`. The component never interprets it — it exists so context-menu
|
|
27
|
+
* actions can target node types through {@link ITreeAction.showFor}.
|
|
28
|
+
*/
|
|
29
|
+
kind?: string;
|
|
11
30
|
/** Child nodes. Only rendered while `isOpen` is `true`. */
|
|
12
31
|
children?: ITreeNode[];
|
|
13
|
-
/** Whether a
|
|
32
|
+
/** Whether a branch is expanded. Controlled by you — see `onToggle`. */
|
|
14
33
|
isOpen?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Icon rendered before the label. Any node works — a `lucide-react` icon, an
|
|
36
|
+
* avatar, a coloured dot. Omit it for a label-only row.
|
|
37
|
+
*/
|
|
38
|
+
icon?: React.ReactNode;
|
|
39
|
+
/** Extra classes merged onto this row. */
|
|
40
|
+
className?: string;
|
|
15
41
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Where a context-menu action applies.
|
|
18
|
-
*
|
|
19
|
-
* `nodeId` is `null` when the menu was opened on empty background, which is how
|
|
20
|
-
* you distinguish "new file here" from "new file at the root".
|
|
21
|
-
*/
|
|
42
|
+
/** Where a context-menu action applies. */
|
|
22
43
|
export interface ITreeContext {
|
|
23
44
|
/** The right-clicked node's id, or `null` for the background. */
|
|
24
45
|
nodeId: string | null;
|
|
25
46
|
/** The right-clicked node, or `null` for the background. */
|
|
26
47
|
node: ITreeNode | null;
|
|
27
48
|
}
|
|
28
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Where a context-menu entry is offered.
|
|
51
|
+
*
|
|
52
|
+
* `'branch'` and `'leaf'` match structurally; `'background'` is a right-click on
|
|
53
|
+
* empty space. Any other string is matched against the node's `kind`, so you can
|
|
54
|
+
* scope an action to your own node types.
|
|
55
|
+
*/
|
|
56
|
+
export type TreeActionScope = 'branch' | 'leaf' | 'background' | (string & {});
|
|
57
|
+
/** An entry in the tree's right-click menu. */
|
|
29
58
|
export interface ITreeAction {
|
|
30
59
|
/** Stable identifier. Used as the React key. */
|
|
31
60
|
id: string;
|
|
@@ -38,39 +67,38 @@ export interface ITreeAction {
|
|
|
38
67
|
/** Draw a separator above this entry. */
|
|
39
68
|
divider?: boolean;
|
|
40
69
|
/**
|
|
41
|
-
* Restrict where the entry appears. Defaults to everywhere.
|
|
42
|
-
*
|
|
70
|
+
* Restrict where the entry appears. Defaults to everywhere.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* showFor: ['branch', 'background'] // structural
|
|
75
|
+
* showFor: ['department'] // matches node.kind
|
|
76
|
+
* ```
|
|
43
77
|
*/
|
|
44
|
-
showFor?:
|
|
78
|
+
showFor?: TreeActionScope[];
|
|
45
79
|
}
|
|
46
80
|
export interface TreeWidgetProps {
|
|
47
81
|
/** Root nodes. Expansion state lives on the nodes themselves via `isOpen`. */
|
|
48
82
|
data: ITreeNode[];
|
|
49
83
|
/**
|
|
50
|
-
* Called when a
|
|
84
|
+
* Called when a branch is clicked. Flip that node's `isOpen` in your own
|
|
51
85
|
* state — the tree renders expansion but does not own it.
|
|
52
86
|
*/
|
|
53
87
|
onToggle?: (node: ITreeNode) => void;
|
|
54
|
-
/** Called when a node is double-clicked
|
|
88
|
+
/** Called when a node is double-clicked or Enter is pressed on a leaf. */
|
|
55
89
|
onActivate?: (node: ITreeNode) => void;
|
|
56
|
-
/** Called after a node is dragged onto a folder. */
|
|
57
|
-
onMoveNode?: (draggedId: string, targetFolderId: string) => void;
|
|
58
90
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
91
|
+
* Called after a node is dragged onto a branch. Only branches accept drops.
|
|
92
|
+
* Reparenting is yours to perform — the tree does not mutate `data`.
|
|
93
|
+
*/
|
|
94
|
+
onMoveNode?: (draggedId: string, targetBranchId: string) => void;
|
|
95
|
+
/**
|
|
96
|
+
* Entries for the right-click menu. Empty by default: the component ships no
|
|
97
|
+
* actions of its own, because "New File" means nothing to an org chart.
|
|
61
98
|
*
|
|
62
|
-
*
|
|
63
|
-
* point for app-specific commands, so the tree itself stays generic.
|
|
99
|
+
* With no entries, no menu appears.
|
|
64
100
|
*/
|
|
65
101
|
actions?: ITreeAction[];
|
|
66
|
-
/** Handler for the built-in "New File" action. */
|
|
67
|
-
onNewFile?: (parentId: string | null) => void;
|
|
68
|
-
/** Handler for the built-in "New Folder" action. */
|
|
69
|
-
onNewFolder?: (parentId: string | null) => void;
|
|
70
|
-
/** Handler for the built-in "Rename" action. */
|
|
71
|
-
onRename?: (nodeId: string) => void;
|
|
72
|
-
/** Handler for the built-in "Delete" action. */
|
|
73
|
-
onDelete?: (nodeId: string) => void;
|
|
74
102
|
/** Indentation added per depth level, in px. Defaults to `12`. */
|
|
75
103
|
indent?: number;
|
|
76
104
|
/**
|
|
@@ -79,32 +107,49 @@ export interface TreeWidgetProps {
|
|
|
79
107
|
* search, or a test environment that can't measure layout. Defaults to `true`.
|
|
80
108
|
*/
|
|
81
109
|
virtualized?: boolean;
|
|
110
|
+
/** Accessible label for the tree. Defaults to `"Tree"`. */
|
|
111
|
+
'aria-label'?: string;
|
|
82
112
|
/** Extra classes merged onto the root element. */
|
|
83
113
|
className?: string;
|
|
84
114
|
}
|
|
85
115
|
/**
|
|
86
|
-
* A virtualised
|
|
116
|
+
* A virtualised, domain-neutral tree with drag-to-move and a data-driven
|
|
117
|
+
* right-click menu.
|
|
87
118
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* `
|
|
91
|
-
* a
|
|
119
|
+
* It renders any hierarchy — files, an org chart, a scene graph, nested
|
|
120
|
+
* categories, an argument map. The component's only structural concept is
|
|
121
|
+
* `isBranch`: branches expand and accept drops, leaves do neither. Everything
|
|
122
|
+
* else — what a node *is*, what icon it carries, what actions it offers — is
|
|
123
|
+
* yours to supply.
|
|
92
124
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
125
|
+
* Rows are virtualised via `react-virtuoso`, so large trees stay responsive.
|
|
126
|
+
*
|
|
127
|
+
* Controlled: expansion lives on your nodes as `isOpen` and changes are
|
|
128
|
+
* reported through `onToggle`, so the tree never holds a second copy of your
|
|
129
|
+
* data.
|
|
96
130
|
*
|
|
97
131
|
* @example
|
|
98
132
|
* ```tsx
|
|
133
|
+
* // An org chart — no files in sight.
|
|
99
134
|
* <TreeWidget
|
|
100
|
-
* data={
|
|
101
|
-
*
|
|
102
|
-
*
|
|
135
|
+
* data={[
|
|
136
|
+
* {
|
|
137
|
+
* id: 'eng',
|
|
138
|
+
* label: 'Engineering',
|
|
139
|
+
* isBranch: true,
|
|
140
|
+
* kind: 'department',
|
|
141
|
+
* isOpen: true,
|
|
142
|
+
* icon: <Building2 size={14} />,
|
|
143
|
+
* children: [
|
|
144
|
+
* { id: 'ada', label: 'Ada Lovelace', kind: 'person', icon: <User size={14} /> },
|
|
145
|
+
* ],
|
|
146
|
+
* },
|
|
147
|
+
* ]}
|
|
148
|
+
* onToggle={(node) => setNodes(toggle(nodes, node.id))}
|
|
103
149
|
* actions={[
|
|
104
|
-
* { id: '
|
|
105
|
-
* showFor: ['folder', 'background'], onSelect: (ctx) => createMap(ctx.nodeId) },
|
|
150
|
+
* { id: 'hire', label: 'Add report', showFor: ['department'], onSelect: addReport },
|
|
106
151
|
* ]}
|
|
107
152
|
* />
|
|
108
153
|
* ```
|
|
109
154
|
*/
|
|
110
|
-
export declare const TreeWidget: ({ data, onToggle, onActivate, onMoveNode, actions,
|
|
155
|
+
export declare const TreeWidget: ({ data, onToggle, onActivate, onMoveNode, actions, indent, virtualized, "aria-label": ariaLabel, className, }: TreeWidgetProps) => import("react/jsx-runtime").JSX.Element;
|
package/dist/core/Boot.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface InitializeShellOptions {
|
|
|
31
31
|
defaultMenus?: boolean;
|
|
32
32
|
}
|
|
33
33
|
/** The command ids `initializeShell` registers unless told not to. */
|
|
34
|
-
export declare const DEFAULT_COMMAND_IDS: readonly ["view.toggleTerminal", "view.toggleChat", "view.toggleSidebar",
|
|
34
|
+
export declare const DEFAULT_COMMAND_IDS: readonly ["view.toggleTerminal", "view.toggleChat", "view.toggleSidebar", ...string[]];
|
|
35
35
|
/**
|
|
36
36
|
* Boot the shell: register commands, menus, panels and status bar items into
|
|
37
37
|
* the shell registries and stores.
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { BundledThemeId } from '../../lib/themes';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A theme id.
|
|
5
|
+
*
|
|
6
|
+
* The bundled ids are suggested by autocomplete, but any string is accepted:
|
|
7
|
+
* define a `.theme-yourname` block with the same custom properties and it works
|
|
8
|
+
* exactly like the built-ins.
|
|
9
|
+
*/
|
|
10
|
+
export type ThemeType = BundledThemeId | (string & {});
|
|
2
11
|
interface ThemeState {
|
|
3
12
|
theme: ThemeType;
|
|
4
13
|
setTheme: (theme: ThemeType) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -57,3 +57,4 @@ export { ThemeSwitcher, type ThemeSwitcherProps, type IThemeOption, DEFAULT_THEM
|
|
|
57
57
|
export { TreeWidget, type TreeWidgetProps, type ITreeNode, type ITreeAction, type ITreeContext } from './components/widgets/TreeWidget';
|
|
58
58
|
export { UserProfile, type UserProfileProps, type IUserProfile, type IUserProfileAction } from './components/widgets/UserProfile';
|
|
59
59
|
export { cn } from './lib/cn';
|
|
60
|
+
export { BUNDLED_THEMES, BUNDLED_THEME_CLASSES, themeClass, type IBundledTheme, type BundledThemeId, } from './lib/themes';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The themes bundled in the library's stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* This is the single source of truth. `ThemeService` derives its union type and
|
|
5
|
+
* its class-swapping from it, `ThemeSwitcher` and `SettingsPanel` derive their
|
|
6
|
+
* default options, `initializeShell` derives its theme commands and View menu,
|
|
7
|
+
* and Storybook derives its toolbar. Adding a theme is a block in `index.css`
|
|
8
|
+
* plus one entry here.
|
|
9
|
+
*
|
|
10
|
+
* It lives in `lib/` rather than `core/services/` because the presentational
|
|
11
|
+
* components need it, and they are forbidden from importing stores.
|
|
12
|
+
*
|
|
13
|
+
* Themes are not a closed set — see {@link ThemeSwitcher}'s `options` prop and
|
|
14
|
+
* {@link SettingsPanel}'s `themes` prop for supplying your own.
|
|
15
|
+
*/
|
|
16
|
+
export interface IBundledTheme {
|
|
17
|
+
/** Matches the `.theme-<id>` class in the stylesheet. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** Full name, for menus and the settings panel. */
|
|
20
|
+
label: string;
|
|
21
|
+
/** Abbreviated name, for the compact segmented switcher. */
|
|
22
|
+
shortLabel: string;
|
|
23
|
+
/** One line on what the theme is, used in documentation. */
|
|
24
|
+
description: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const BUNDLED_THEMES: readonly [{
|
|
27
|
+
readonly id: "light";
|
|
28
|
+
readonly label: "Light";
|
|
29
|
+
readonly shortLabel: "Light";
|
|
30
|
+
readonly description: "Neutral light theme. The default.";
|
|
31
|
+
}, {
|
|
32
|
+
readonly id: "dark";
|
|
33
|
+
readonly label: "Dark";
|
|
34
|
+
readonly shortLabel: "Dark";
|
|
35
|
+
readonly description: "Low-glare dark theme on a soft grey base.";
|
|
36
|
+
}, {
|
|
37
|
+
readonly id: "gt";
|
|
38
|
+
readonly label: "Georgia Tech";
|
|
39
|
+
readonly shortLabel: "GT";
|
|
40
|
+
readonly description: "Buzz Gold (#EAAA00) on Tech Tower White.";
|
|
41
|
+
}, {
|
|
42
|
+
readonly id: "tamu";
|
|
43
|
+
readonly label: "Texas A&M";
|
|
44
|
+
readonly shortLabel: "TAMU";
|
|
45
|
+
readonly description: "Aggie Maroon (#500000) on white.";
|
|
46
|
+
}];
|
|
47
|
+
/** Ids of the themes that ship in the stylesheet. */
|
|
48
|
+
export type BundledThemeId = (typeof BUNDLED_THEMES)[number]['id'];
|
|
49
|
+
/** Every bundled theme's class name, e.g. `["theme-light", …]`. */
|
|
50
|
+
export declare const BUNDLED_THEME_CLASSES: string[];
|
|
51
|
+
/**
|
|
52
|
+
* The class name for a theme id.
|
|
53
|
+
*
|
|
54
|
+
* Accepts any string, not just a bundled id, so an app that defines its own
|
|
55
|
+
* `.theme-*` block can use it too.
|
|
56
|
+
*/
|
|
57
|
+
export declare const themeClass: (id: string) => string;
|