@textui/widgets 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/chart/area-chart.d.ts +4 -0
- package/dist/chart/area-chart.d.ts.map +1 -0
- package/dist/chart/area-chart.js +3 -0
- package/dist/chart/bar-chart.d.ts +32 -0
- package/dist/chart/bar-chart.d.ts.map +1 -0
- package/dist/chart/bar-chart.js +32 -0
- package/dist/chart/gauge.d.ts +26 -0
- package/dist/chart/gauge.d.ts.map +1 -0
- package/dist/chart/gauge.js +26 -0
- package/dist/chart/heatmap.d.ts +13 -0
- package/dist/chart/heatmap.d.ts.map +1 -0
- package/dist/chart/heatmap.js +27 -0
- package/dist/chart/histogram.d.ts +12 -0
- package/dist/chart/histogram.d.ts.map +1 -0
- package/dist/chart/histogram.js +22 -0
- package/dist/chart/index.d.ts +20 -0
- package/dist/chart/index.d.ts.map +1 -0
- package/dist/chart/index.js +32 -0
- package/dist/chart/line-chart.d.ts +7 -0
- package/dist/chart/line-chart.d.ts.map +1 -0
- package/dist/chart/line-chart.js +75 -0
- package/dist/chart/shared.d.ts +34 -0
- package/dist/chart/shared.d.ts.map +1 -0
- package/dist/chart/shared.js +78 -0
- package/dist/chart/sparkline.d.ts +15 -0
- package/dist/chart/sparkline.d.ts.map +1 -0
- package/dist/chart/sparkline.js +22 -0
- package/dist/control/button.d.ts +27 -0
- package/dist/control/button.d.ts.map +1 -0
- package/dist/control/button.js +130 -0
- package/dist/control/checkbox.d.ts +10 -0
- package/dist/control/checkbox.d.ts.map +1 -0
- package/dist/control/checkbox.js +31 -0
- package/dist/control/index.d.ts +20 -0
- package/dist/control/index.d.ts.map +1 -0
- package/dist/control/index.js +36 -0
- package/dist/control/radio-group.d.ts +17 -0
- package/dist/control/radio-group.d.ts.map +1 -0
- package/dist/control/radio-group.js +32 -0
- package/dist/control/search-box.d.ts +7 -0
- package/dist/control/search-box.d.ts.map +1 -0
- package/dist/control/search-box.js +3 -0
- package/dist/control/select.d.ts +42 -0
- package/dist/control/select.d.ts.map +1 -0
- package/dist/control/select.js +166 -0
- package/dist/control/shared.d.ts +41 -0
- package/dist/control/shared.d.ts.map +1 -0
- package/dist/control/shared.js +1 -0
- package/dist/control/slider.d.ts +14 -0
- package/dist/control/slider.d.ts.map +1 -0
- package/dist/control/slider.js +45 -0
- package/dist/control/switch.d.ts +10 -0
- package/dist/control/switch.d.ts.map +1 -0
- package/dist/control/switch.js +32 -0
- package/dist/control/text-area.d.ts +87 -0
- package/dist/control/text-area.d.ts.map +1 -0
- package/dist/control/text-area.js +299 -0
- package/dist/control/text-input.d.ts +10 -0
- package/dist/control/text-input.d.ts.map +1 -0
- package/dist/control/text-input.js +129 -0
- package/dist/data/code-viewer.d.ts +71 -0
- package/dist/data/code-viewer.d.ts.map +1 -0
- package/dist/data/code-viewer.js +232 -0
- package/dist/data/feed.d.ts +43 -0
- package/dist/data/feed.d.ts.map +1 -0
- package/dist/data/feed.js +178 -0
- package/dist/data/index.d.ts +54 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +187 -0
- package/dist/data/list.d.ts +31 -0
- package/dist/data/list.d.ts.map +1 -0
- package/dist/data/list.js +104 -0
- package/dist/data/log-viewer.d.ts +26 -0
- package/dist/data/log-viewer.d.ts.map +1 -0
- package/dist/data/log-viewer.js +69 -0
- package/dist/data/markdown-view.d.ts +31 -0
- package/dist/data/markdown-view.d.ts.map +1 -0
- package/dist/data/markdown-view.js +139 -0
- package/dist/data/pagination.d.ts +9 -0
- package/dist/data/pagination.d.ts.map +1 -0
- package/dist/data/pagination.js +18 -0
- package/dist/data/scroll-thumb.d.ts +15 -0
- package/dist/data/scroll-thumb.d.ts.map +1 -0
- package/dist/data/scroll-thumb.js +32 -0
- package/dist/data/shared.d.ts +12 -0
- package/dist/data/shared.d.ts.map +1 -0
- package/dist/data/shared.js +17 -0
- package/dist/data/tree.d.ts +35 -0
- package/dist/data/tree.d.ts.map +1 -0
- package/dist/data/tree.js +158 -0
- package/dist/decorations.d.ts +88 -0
- package/dist/decorations.d.ts.map +1 -0
- package/dist/decorations.js +135 -0
- package/dist/display/alert.d.ts +8 -0
- package/dist/display/alert.d.ts.map +1 -0
- package/dist/display/alert.js +12 -0
- package/dist/display/badge.d.ts +10 -0
- package/dist/display/badge.d.ts.map +1 -0
- package/dist/display/badge.js +21 -0
- package/dist/display/card.d.ts +8 -0
- package/dist/display/card.d.ts.map +1 -0
- package/dist/display/card.js +12 -0
- package/dist/display/empty-state.d.ts +10 -0
- package/dist/display/empty-state.d.ts.map +1 -0
- package/dist/display/empty-state.js +5 -0
- package/dist/display/error-state.d.ts +9 -0
- package/dist/display/error-state.d.ts.map +1 -0
- package/dist/display/error-state.js +7 -0
- package/dist/display/heading.d.ts +6 -0
- package/dist/display/heading.d.ts.map +1 -0
- package/dist/display/heading.js +8 -0
- package/dist/display/index.d.ts +25 -0
- package/dist/display/index.d.ts.map +1 -0
- package/dist/display/index.js +52 -0
- package/dist/display/key-value.d.ts +14 -0
- package/dist/display/key-value.d.ts.map +1 -0
- package/dist/display/key-value.js +11 -0
- package/dist/display/label.d.ts +6 -0
- package/dist/display/label.d.ts.map +1 -0
- package/dist/display/label.js +3 -0
- package/dist/display/marquee.d.ts +36 -0
- package/dist/display/marquee.d.ts.map +1 -0
- package/dist/display/marquee.js +55 -0
- package/dist/display/progress.d.ts +39 -0
- package/dist/display/progress.d.ts.map +1 -0
- package/dist/display/progress.js +46 -0
- package/dist/display/skeleton.d.ts +20 -0
- package/dist/display/skeleton.d.ts.map +1 -0
- package/dist/display/skeleton.js +20 -0
- package/dist/display/spinner.d.ts +7 -0
- package/dist/display/spinner.d.ts.map +1 -0
- package/dist/display/spinner.js +9 -0
- package/dist/display/status-dot.d.ts +8 -0
- package/dist/display/status-dot.d.ts.map +1 -0
- package/dist/display/status-dot.js +13 -0
- package/dist/display/timeline.d.ts +12 -0
- package/dist/display/timeline.d.ts.map +1 -0
- package/dist/display/timeline.js +22 -0
- package/dist/find.d.ts +74 -0
- package/dist/find.d.ts.map +1 -0
- package/dist/find.js +102 -0
- package/dist/form/danger-zone.d.ts +11 -0
- package/dist/form/danger-zone.d.ts.map +1 -0
- package/dist/form/danger-zone.js +15 -0
- package/dist/form/field.d.ts +19 -0
- package/dist/form/field.d.ts.map +1 -0
- package/dist/form/field.js +35 -0
- package/dist/form/form-actions.d.ts +11 -0
- package/dist/form/form-actions.d.ts.map +1 -0
- package/dist/form/form-actions.js +16 -0
- package/dist/form/form-section.d.ts +7 -0
- package/dist/form/form-section.d.ts.map +1 -0
- package/dist/form/form-section.js +5 -0
- package/dist/form/form.d.ts +7 -0
- package/dist/form/form.d.ts.map +1 -0
- package/dist/form/form.js +3 -0
- package/dist/form/index.d.ts +42 -0
- package/dist/form/index.d.ts.map +1 -0
- package/dist/form/index.js +140 -0
- package/dist/form/shared.d.ts +25 -0
- package/dist/form/shared.d.ts.map +1 -0
- package/dist/form/shared.js +9 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/layout/center.d.ts +7 -0
- package/dist/layout/center.d.ts.map +1 -0
- package/dist/layout/center.js +8 -0
- package/dist/layout/column.d.ts +3 -0
- package/dist/layout/column.d.ts.map +1 -0
- package/dist/layout/column.js +2 -0
- package/dist/layout/divider.d.ts +11 -0
- package/dist/layout/divider.d.ts.map +1 -0
- package/dist/layout/divider.js +13 -0
- package/dist/layout/grid.d.ts +10 -0
- package/dist/layout/grid.d.ts.map +1 -0
- package/dist/layout/grid.js +22 -0
- package/dist/layout/index.d.ts +19 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +36 -0
- package/dist/layout/panel.d.ts +37 -0
- package/dist/layout/panel.d.ts.map +1 -0
- package/dist/layout/panel.js +71 -0
- package/dist/layout/row.d.ts +7 -0
- package/dist/layout/row.d.ts.map +1 -0
- package/dist/layout/row.js +2 -0
- package/dist/layout/scroll-view.d.ts +27 -0
- package/dist/layout/scroll-view.d.ts.map +1 -0
- package/dist/layout/scroll-view.js +93 -0
- package/dist/layout/splitter.d.ts +10 -0
- package/dist/layout/splitter.d.ts.map +1 -0
- package/dist/layout/splitter.js +9 -0
- package/dist/layout/stack.d.ts +7 -0
- package/dist/layout/stack.d.ts.map +1 -0
- package/dist/layout/stack.js +5 -0
- package/dist/navigation/breadcrumb.d.ts +14 -0
- package/dist/navigation/breadcrumb.d.ts.map +1 -0
- package/dist/navigation/breadcrumb.js +24 -0
- package/dist/navigation/index.d.ts +17 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +30 -0
- package/dist/navigation/key-hints.d.ts +11 -0
- package/dist/navigation/key-hints.d.ts.map +1 -0
- package/dist/navigation/key-hints.js +13 -0
- package/dist/navigation/menu.d.ts +35 -0
- package/dist/navigation/menu.d.ts.map +1 -0
- package/dist/navigation/menu.js +88 -0
- package/dist/navigation/status-bar.d.ts +18 -0
- package/dist/navigation/status-bar.d.ts.map +1 -0
- package/dist/navigation/status-bar.js +12 -0
- package/dist/navigation/tabs.d.ts +20 -0
- package/dist/navigation/tabs.d.ts.map +1 -0
- package/dist/navigation/tabs.js +41 -0
- package/dist/navigation/toolbar.d.ts +14 -0
- package/dist/navigation/toolbar.d.ts.map +1 -0
- package/dist/navigation/toolbar.js +10 -0
- package/dist/navigation/wizard.d.ts +15 -0
- package/dist/navigation/wizard.d.ts.map +1 -0
- package/dist/navigation/wizard.js +17 -0
- package/dist/overlay/command-palette.d.ts +63 -0
- package/dist/overlay/command-palette.d.ts.map +1 -0
- package/dist/overlay/command-palette.js +415 -0
- package/dist/overlay/dialog.d.ts +16 -0
- package/dist/overlay/dialog.d.ts.map +1 -0
- package/dist/overlay/dialog.js +44 -0
- package/dist/overlay/index.d.ts +84 -0
- package/dist/overlay/index.d.ts.map +1 -0
- package/dist/overlay/index.js +157 -0
- package/dist/overlay/layer-scope.d.ts +21 -0
- package/dist/overlay/layer-scope.d.ts.map +1 -0
- package/dist/overlay/layer-scope.js +5 -0
- package/dist/overlay/path-picker.d.ts +14 -0
- package/dist/overlay/path-picker.d.ts.map +1 -0
- package/dist/overlay/path-picker.js +200 -0
- package/dist/overlay/prompt-dialog.d.ts +12 -0
- package/dist/overlay/prompt-dialog.d.ts.map +1 -0
- package/dist/overlay/prompt-dialog.js +29 -0
- package/dist/overlay/shared.d.ts +17 -0
- package/dist/overlay/shared.d.ts.map +1 -0
- package/dist/overlay/shared.js +17 -0
- package/dist/overlay/toast-host.d.ts +8 -0
- package/dist/overlay/toast-host.d.ts.map +1 -0
- package/dist/overlay/toast-host.js +19 -0
- package/dist/overlay/toast.d.ts +9 -0
- package/dist/overlay/toast.d.ts.map +1 -0
- package/dist/overlay/toast.js +20 -0
- package/dist/overlay/tooltip.d.ts +6 -0
- package/dist/overlay/tooltip.d.ts.map +1 -0
- package/dist/overlay/tooltip.js +11 -0
- package/dist/panel/index.d.ts +79 -0
- package/dist/panel/index.d.ts.map +1 -0
- package/dist/panel/index.js +256 -0
- package/dist/panel/resource-panel.d.ts +93 -0
- package/dist/panel/resource-panel.d.ts.map +1 -0
- package/dist/panel/resource-panel.js +213 -0
- package/dist/panel/shared.d.ts +6 -0
- package/dist/panel/shared.d.ts.map +1 -0
- package/dist/panel/shared.js +4 -0
- package/dist/shells/console-shell.d.ts +8 -0
- package/dist/shells/console-shell.d.ts.map +1 -0
- package/dist/shells/console-shell.js +30 -0
- package/dist/shells/index.d.ts +20 -0
- package/dist/shells/index.d.ts.map +1 -0
- package/dist/shells/index.js +61 -0
- package/dist/shells/paper-shell.d.ts +9 -0
- package/dist/shells/paper-shell.d.ts.map +1 -0
- package/dist/shells/paper-shell.js +23 -0
- package/dist/shells/plain-shell.d.ts +3 -0
- package/dist/shells/plain-shell.d.ts.map +1 -0
- package/dist/shells/plain-shell.js +11 -0
- package/dist/shells/shared.d.ts +12 -0
- package/dist/shells/shared.d.ts.map +1 -0
- package/dist/shells/shared.js +21 -0
- package/dist/shells/workbench-shell.d.ts +9 -0
- package/dist/shells/workbench-shell.d.ts.map +1 -0
- package/dist/shells/workbench-shell.js +43 -0
- package/dist/surface/bar-layout.d.ts +4 -0
- package/dist/surface/bar-layout.d.ts.map +1 -0
- package/dist/surface/bar-layout.js +7 -0
- package/dist/surface/index.d.ts +58 -0
- package/dist/surface/index.d.ts.map +1 -0
- package/dist/surface/index.js +48 -0
- package/dist/surface/inline-layout.d.ts +4 -0
- package/dist/surface/inline-layout.d.ts.map +1 -0
- package/dist/surface/inline-layout.js +7 -0
- package/dist/surface/mount-view.d.ts +7 -0
- package/dist/surface/mount-view.d.ts.map +1 -0
- package/dist/surface/mount-view.js +19 -0
- package/dist/surface/rail-layout.d.ts +4 -0
- package/dist/surface/rail-layout.d.ts.map +1 -0
- package/dist/surface/rail-layout.js +19 -0
- package/dist/surface/shared.d.ts +7 -0
- package/dist/surface/shared.d.ts.map +1 -0
- package/dist/surface/shared.js +1 -0
- package/dist/surface/single-layout.d.ts +4 -0
- package/dist/surface/single-layout.d.ts.map +1 -0
- package/dist/surface/single-layout.js +18 -0
- package/dist/surface/split-layout.d.ts +4 -0
- package/dist/surface/split-layout.d.ts.map +1 -0
- package/dist/surface/split-layout.js +7 -0
- package/dist/surface/stack-layout.d.ts +4 -0
- package/dist/surface/stack-layout.d.ts.map +1 -0
- package/dist/surface/stack-layout.js +22 -0
- package/dist/surface/surface-area.d.ts +16 -0
- package/dist/surface/surface-area.d.ts.map +1 -0
- package/dist/surface/surface-area.js +32 -0
- package/dist/surface/tabs-layout.d.ts +4 -0
- package/dist/surface/tabs-layout.d.ts.map +1 -0
- package/dist/surface/tabs-layout.js +24 -0
- package/dist/tone.d.ts +16 -0
- package/dist/tone.d.ts.map +1 -0
- package/dist/tone.js +45 -0
- package/dist/viewport.d.ts +30 -0
- package/dist/viewport.d.ts.map +1 -0
- package/dist/viewport.js +17 -0
- package/package.json +54 -0
- package/src/chart/area-chart.ts +9 -0
- package/src/chart/bar-chart.ts +74 -0
- package/src/chart/gauge.ts +60 -0
- package/src/chart/heatmap.ts +49 -0
- package/src/chart/histogram.ts +36 -0
- package/src/chart/index.ts +36 -0
- package/src/chart/line-chart.ts +100 -0
- package/src/chart/shared.ts +112 -0
- package/src/chart/sparkline.ts +46 -0
- package/src/control/button.ts +184 -0
- package/src/control/checkbox.ts +48 -0
- package/src/control/index.ts +40 -0
- package/src/control/radio-group.ts +60 -0
- package/src/control/search-box.ts +15 -0
- package/src/control/select.ts +230 -0
- package/src/control/shared.ts +41 -0
- package/src/control/slider.ts +63 -0
- package/src/control/switch.ts +39 -0
- package/src/control/text-area.ts +390 -0
- package/src/control/text-input.ts +150 -0
- package/src/data/code-viewer.ts +337 -0
- package/src/data/feed.ts +204 -0
- package/src/data/index.ts +243 -0
- package/src/data/list.ts +149 -0
- package/src/data/log-viewer.ts +105 -0
- package/src/data/markdown-view.ts +182 -0
- package/src/data/pagination.ts +31 -0
- package/src/data/scroll-thumb.ts +40 -0
- package/src/data/shared.ts +23 -0
- package/src/data/tree.ts +215 -0
- package/src/decorations.ts +176 -0
- package/src/display/alert.ts +28 -0
- package/src/display/badge.ts +40 -0
- package/src/display/card.ts +23 -0
- package/src/display/empty-state.ts +21 -0
- package/src/display/error-state.ts +23 -0
- package/src/display/heading.ts +17 -0
- package/src/display/index.ts +55 -0
- package/src/display/key-value.ts +32 -0
- package/src/display/label.ts +11 -0
- package/src/display/marquee.ts +88 -0
- package/src/display/progress.ts +92 -0
- package/src/display/skeleton.ts +34 -0
- package/src/display/spinner.ts +20 -0
- package/src/display/status-dot.ts +24 -0
- package/src/display/timeline.ts +47 -0
- package/src/find.ts +138 -0
- package/src/form/danger-zone.ts +34 -0
- package/src/form/field.ts +61 -0
- package/src/form/form-actions.ts +35 -0
- package/src/form/form-section.ts +16 -0
- package/src/form/form.ts +13 -0
- package/src/form/index.ts +191 -0
- package/src/form/shared.ts +36 -0
- package/src/index.ts +63 -0
- package/src/layout/center.ts +17 -0
- package/src/layout/column.ts +6 -0
- package/src/layout/divider.ts +31 -0
- package/src/layout/grid.ts +34 -0
- package/src/layout/index.ts +39 -0
- package/src/layout/panel.ts +114 -0
- package/src/layout/row.ts +11 -0
- package/src/layout/scroll-view.ts +115 -0
- package/src/layout/splitter.ts +24 -0
- package/src/layout/stack.ts +12 -0
- package/src/navigation/breadcrumb.ts +43 -0
- package/src/navigation/index.ts +33 -0
- package/src/navigation/key-hints.ts +25 -0
- package/src/navigation/menu.ts +134 -0
- package/src/navigation/status-bar.ts +43 -0
- package/src/navigation/tabs.ts +73 -0
- package/src/navigation/toolbar.ts +25 -0
- package/src/navigation/wizard.ts +43 -0
- package/src/overlay/command-palette.ts +485 -0
- package/src/overlay/dialog.ts +65 -0
- package/src/overlay/index.ts +232 -0
- package/src/overlay/layer-scope.ts +26 -0
- package/src/overlay/path-picker.ts +215 -0
- package/src/overlay/prompt-dialog.ts +45 -0
- package/src/overlay/shared.ts +19 -0
- package/src/overlay/toast-host.ts +30 -0
- package/src/overlay/toast.ts +35 -0
- package/src/overlay/tooltip.ts +17 -0
- package/src/panel/index.ts +317 -0
- package/src/panel/resource-panel.ts +304 -0
- package/src/panel/shared.ts +8 -0
- package/src/shells/console-shell.ts +47 -0
- package/src/shells/index.ts +66 -0
- package/src/shells/paper-shell.ts +31 -0
- package/src/shells/plain-shell.ts +19 -0
- package/src/shells/shared.ts +24 -0
- package/src/shells/workbench-shell.ts +62 -0
- package/src/surface/bar-layout.ts +11 -0
- package/src/surface/index.ts +53 -0
- package/src/surface/inline-layout.ts +11 -0
- package/src/surface/mount-view.ts +30 -0
- package/src/surface/rail-layout.ts +27 -0
- package/src/surface/shared.ts +7 -0
- package/src/surface/single-layout.ts +25 -0
- package/src/surface/split-layout.ts +11 -0
- package/src/surface/stack-layout.ts +32 -0
- package/src/surface/surface-area.ts +46 -0
- package/src/surface/tabs-layout.ts +30 -0
- package/src/tone.ts +48 -0
- package/src/viewport.ts +45 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ComponentDefinition, ShellDefinition } from '@textui/core';
|
|
2
|
+
import { ConsoleShell } from './console-shell.js';
|
|
3
|
+
import { PaperShell } from './paper-shell.js';
|
|
4
|
+
import { PlainShell } from './plain-shell.js';
|
|
5
|
+
import { WorkbenchShell } from './workbench-shell.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shells.
|
|
9
|
+
*
|
|
10
|
+
* A shell decides where the surfaces go, and that is the whole of what it
|
|
11
|
+
* decides. The three below are the same application in three house styles -
|
|
12
|
+
* a dense bordered console, an airy report, and a workbench frame - and
|
|
13
|
+
* nothing in the component catalog knows which one is mounted.
|
|
14
|
+
*
|
|
15
|
+
* That is the test the whole architecture exists to pass: if a shell needs a
|
|
16
|
+
* component the others cannot use, the boundary is in the wrong place.
|
|
17
|
+
*/
|
|
18
|
+
export * from './console-shell.js';
|
|
19
|
+
export * from './paper-shell.js';
|
|
20
|
+
export * from './plain-shell.js';
|
|
21
|
+
export * from './workbench-shell.js';
|
|
22
|
+
export type { ShellProps } from './shared.js';
|
|
23
|
+
|
|
24
|
+
export const SHELL_COMPONENTS: ComponentDefinition[] = [
|
|
25
|
+
{ component: 'PlainShell', category: 'chrome', renderer: { kind: 'function', render: PlainShell }, description: 'Main and status, no frame.' },
|
|
26
|
+
{ component: 'ConsoleShell', category: 'chrome', renderer: { kind: 'function', render: ConsoleShell }, description: 'Dense bordered operator console.' },
|
|
27
|
+
{ component: 'PaperShell', category: 'chrome', renderer: { kind: 'function', render: PaperShell }, description: 'Airy and borderless.' },
|
|
28
|
+
{ component: 'WorkbenchShell', category: 'chrome', renderer: { kind: 'function', render: WorkbenchShell }, description: 'Full frame: rail, sidebar, tabs, panel, status.' },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export const BUILTIN_SHELLS: ShellDefinition[] = [
|
|
32
|
+
{
|
|
33
|
+
id: 'plain',
|
|
34
|
+
title: 'Plain',
|
|
35
|
+
description: 'Main and status only. The default.',
|
|
36
|
+
component: 'PlainShell',
|
|
37
|
+
surfaces: ['main', 'status', 'overlay', 'notify'],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'console',
|
|
41
|
+
title: 'Console',
|
|
42
|
+
description: 'Dense, bordered, every region labelled.',
|
|
43
|
+
component: 'ConsoleShell',
|
|
44
|
+
theme: 'console',
|
|
45
|
+
surfaces: ['header', 'sidebar', 'main', 'panel', 'aside', 'status', 'overlay', 'notify'],
|
|
46
|
+
minSize: { width: 60, height: 12 },
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'paper',
|
|
50
|
+
title: 'Paper',
|
|
51
|
+
description: 'Borderless. Whitespace and alignment do the separating.',
|
|
52
|
+
component: 'PaperShell',
|
|
53
|
+
theme: 'paper',
|
|
54
|
+
surfaces: ['header', 'main', 'panel', 'status', 'overlay', 'notify'],
|
|
55
|
+
minSize: { width: 50, height: 10 },
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'workbench',
|
|
59
|
+
title: 'Workbench',
|
|
60
|
+
description: 'Persistent frame with a rail, sidebar, tabs and a panel.',
|
|
61
|
+
component: 'WorkbenchShell',
|
|
62
|
+
theme: 'workbench',
|
|
63
|
+
surfaces: ['header', 'rail', 'sidebar', 'main', 'panel', 'aside', 'status', 'overlay', 'notify'],
|
|
64
|
+
minSize: { width: 80, height: 20 },
|
|
65
|
+
},
|
|
66
|
+
];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineComponent, h, useTheme } from '@textui/core';
|
|
2
|
+
import { ToastHost } from '../overlay/index.js';
|
|
3
|
+
import { SurfaceArea } from '../surface/index.js';
|
|
4
|
+
import type { ShellProps } from './shared.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Direction B: airy and borderless.
|
|
8
|
+
*
|
|
9
|
+
* Whitespace does the separating; there is no frame, and the surfaces that
|
|
10
|
+
* would be chrome elsewhere are simply spaced sections. Also the shell that
|
|
11
|
+
* reads best when the output is piped to a file.
|
|
12
|
+
*/
|
|
13
|
+
export const PaperShell = defineComponent<ShellProps>('PaperShell', (props) => {
|
|
14
|
+
const theme = useTheme();
|
|
15
|
+
return h('box', {
|
|
16
|
+
direction: 'column',
|
|
17
|
+
width: '100%',
|
|
18
|
+
height: '100%',
|
|
19
|
+
bg: 'canvas',
|
|
20
|
+
fg: 'text',
|
|
21
|
+
padding: [1, theme.spacing.md],
|
|
22
|
+
gap: 1,
|
|
23
|
+
...props,
|
|
24
|
+
},
|
|
25
|
+
h(SurfaceArea, { surface: 'header' }),
|
|
26
|
+
h(SurfaceArea, { surface: 'main', flex: 1 }),
|
|
27
|
+
h(SurfaceArea, { surface: 'panel' }),
|
|
28
|
+
h(SurfaceArea, { surface: 'status' }),
|
|
29
|
+
h(ToastHost, { anchor: 'bottom' }),
|
|
30
|
+
);
|
|
31
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineComponent, h } from '@textui/core';
|
|
2
|
+
import { ToastHost } from '../overlay/index.js';
|
|
3
|
+
import { SurfaceArea } from '../surface/index.js';
|
|
4
|
+
import type { ShellProps } from './shared.js';
|
|
5
|
+
|
|
6
|
+
/** No chrome at all: `main` fills the terminal. For a single-screen tool. */
|
|
7
|
+
export const PlainShell = defineComponent<ShellProps>('PlainShell', (props) =>
|
|
8
|
+
h('box', {
|
|
9
|
+
direction: 'column', width: '100%', height: '100%',
|
|
10
|
+
// Ink as well as page: a themed background with the terminal's own
|
|
11
|
+
// foreground on top is how a light theme comes out white on white.
|
|
12
|
+
bg: 'canvas', fg: 'text',
|
|
13
|
+
...props,
|
|
14
|
+
},
|
|
15
|
+
h(SurfaceArea, { surface: 'main', flex: 1 }),
|
|
16
|
+
h(SurfaceArea, { surface: 'status' }),
|
|
17
|
+
h(ToastHost, { anchor: 'bottom-right' }),
|
|
18
|
+
),
|
|
19
|
+
);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BindingPath, BoxProps, SurfaceName } from '@textui/core';
|
|
2
|
+
import { useRuntime, useStoreSubtree } from '@textui/core';
|
|
3
|
+
|
|
4
|
+
export type ShellProps = BoxProps;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Whether a surface has anything to show.
|
|
8
|
+
*
|
|
9
|
+
* `SurfaceArea` already renders nothing for an empty surface, but a region
|
|
10
|
+
* with a fixed width reserves its column either way - which is how an
|
|
11
|
+
* application that never mounts a sidebar still gets an empty gutter down the
|
|
12
|
+
* left of every screen. A shell has to ask before it spends the space.
|
|
13
|
+
*/
|
|
14
|
+
export function useSurfaceMounted(surface: SurfaceName): boolean {
|
|
15
|
+
const runtime = useRuntime();
|
|
16
|
+
useStoreSubtree(`$/layout/surfaces/${surface}` as BindingPath);
|
|
17
|
+
useStoreSubtree(`$/layout/mounts/${surface}` as BindingPath);
|
|
18
|
+
|
|
19
|
+
const app = runtime.app();
|
|
20
|
+
if (!app) return false;
|
|
21
|
+
const state = app.surfaces.state(surface);
|
|
22
|
+
if (!state.visible || state.collapsed) return false;
|
|
23
|
+
return app.surfaces.mounts(surface).length > 0;
|
|
24
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { defineComponent, h, useSize, useStoreValue, useTheme } from '@textui/core';
|
|
2
|
+
import { ToastHost } from '../overlay/index.js';
|
|
3
|
+
import { SurfaceArea } from '../surface/index.js';
|
|
4
|
+
import type { ShellProps } from './shared.js';
|
|
5
|
+
import { useSurfaceMounted } from './shared.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Direction C: the workbench.
|
|
9
|
+
*
|
|
10
|
+
* A persistent frame - rail, sidebar, tabbed main, panel, status - inside a
|
|
11
|
+
* rounded border. The richest shell, and the one that needs the most room, so
|
|
12
|
+
* it declares a minimum size and the app falls back when the terminal is small.
|
|
13
|
+
*/
|
|
14
|
+
export const WorkbenchShell = defineComponent<ShellProps>('WorkbenchShell', (props) => {
|
|
15
|
+
const theme = useTheme();
|
|
16
|
+
const size = useSize();
|
|
17
|
+
const sidebarCollapsed = useStoreValue<boolean>('$/ui/sidebar/collapsed', false);
|
|
18
|
+
const asideVisible = useStoreValue<boolean>('$/ui/aside/visible', false);
|
|
19
|
+
const sidebar = useSurfaceMounted('sidebar');
|
|
20
|
+
const aside = useSurfaceMounted('aside');
|
|
21
|
+
const narrow = size.width < 90;
|
|
22
|
+
|
|
23
|
+
return h('box', {
|
|
24
|
+
direction: 'column',
|
|
25
|
+
width: '100%',
|
|
26
|
+
height: '100%',
|
|
27
|
+
bg: 'canvas',
|
|
28
|
+
fg: 'text',
|
|
29
|
+
border: theme.border,
|
|
30
|
+
...props,
|
|
31
|
+
},
|
|
32
|
+
h(SurfaceArea, { surface: 'header' }),
|
|
33
|
+
|
|
34
|
+
h('box', { direction: 'row', flex: 1 },
|
|
35
|
+
h(SurfaceArea, { surface: 'rail' }),
|
|
36
|
+
|
|
37
|
+
sidebar && !sidebarCollapsed && !narrow
|
|
38
|
+
? h('box', {
|
|
39
|
+
width: 24,
|
|
40
|
+
border: { style: theme.border, sides: { right: true } },
|
|
41
|
+
direction: 'column',
|
|
42
|
+
padding: { left: 1 },
|
|
43
|
+
}, h(SurfaceArea, { surface: 'sidebar', flex: 1 }))
|
|
44
|
+
: null,
|
|
45
|
+
|
|
46
|
+
h('box', { flex: 1, direction: 'column', padding: { left: 1 } },
|
|
47
|
+
h(SurfaceArea, { surface: 'main', flex: 1 }),
|
|
48
|
+
h(SurfaceArea, { surface: 'panel' })),
|
|
49
|
+
|
|
50
|
+
aside && asideVisible && !narrow
|
|
51
|
+
? h('box', {
|
|
52
|
+
width: 30,
|
|
53
|
+
border: { style: theme.border, sides: { left: true } },
|
|
54
|
+
direction: 'column',
|
|
55
|
+
padding: { left: 1 },
|
|
56
|
+
}, h(SurfaceArea, { surface: 'aside', flex: 1 }))
|
|
57
|
+
: null),
|
|
58
|
+
|
|
59
|
+
h(SurfaceArea, { surface: 'status' }),
|
|
60
|
+
h(ToastHost, { anchor: 'bottom-right' }),
|
|
61
|
+
);
|
|
62
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineComponent, h } from '@textui/core';
|
|
2
|
+
import { MountView } from './mount-view.js';
|
|
3
|
+
import type { LayoutProps } from './shared.js';
|
|
4
|
+
|
|
5
|
+
/** Every mount on one line. Headers and status bars use this. */
|
|
6
|
+
export const BarLayout = defineComponent<LayoutProps>('BarLayout', (props) => {
|
|
7
|
+
const { mounts, surface: _surface, state: _state, ...rest } = props;
|
|
8
|
+
return h('box', { direction: 'row', height: 1, gap: 2, ...rest },
|
|
9
|
+
...mounts.map((mount) => h(MountView, { key: mount.key, mount })),
|
|
10
|
+
);
|
|
11
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ComponentDefinition } from '@textui/core';
|
|
2
|
+
import { BarLayout } from './bar-layout.js';
|
|
3
|
+
import { InlineLayout } from './inline-layout.js';
|
|
4
|
+
import { MountView } from './mount-view.js';
|
|
5
|
+
import { RailLayout } from './rail-layout.js';
|
|
6
|
+
import { SingleLayout } from './single-layout.js';
|
|
7
|
+
import { SplitLayout } from './split-layout.js';
|
|
8
|
+
import { StackLayout } from './stack-layout.js';
|
|
9
|
+
import { SurfaceArea } from './surface-area.js';
|
|
10
|
+
import { TabsLayout } from './tabs-layout.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Surfaces, mounts and layouts.
|
|
14
|
+
*
|
|
15
|
+
* `SurfaceArea` is the only bridge between the registries and the render tree:
|
|
16
|
+
* it reads a surface's mounts and its state, then hands both to whichever
|
|
17
|
+
* layout that surface is currently using. Because the layout is store state
|
|
18
|
+
* rather than code, a sidebar can be tabs now and a stack a keystroke later.
|
|
19
|
+
*/
|
|
20
|
+
export * from './bar-layout.js';
|
|
21
|
+
export * from './inline-layout.js';
|
|
22
|
+
export * from './mount-view.js';
|
|
23
|
+
export * from './rail-layout.js';
|
|
24
|
+
export * from './single-layout.js';
|
|
25
|
+
export * from './split-layout.js';
|
|
26
|
+
export * from './stack-layout.js';
|
|
27
|
+
export * from './surface-area.js';
|
|
28
|
+
export * from './tabs-layout.js';
|
|
29
|
+
export type { LayoutProps } from './shared.js';
|
|
30
|
+
|
|
31
|
+
export const SURFACE_COMPONENTS: ComponentDefinition[] = [
|
|
32
|
+
{ component: 'SurfaceArea', category: 'chrome', renderer: { kind: 'function', render: SurfaceArea }, description: 'Renders one surface through its active layout.' },
|
|
33
|
+
{ component: 'MountView', category: 'chrome', renderer: { kind: 'function', render: MountView }, description: 'Renders one mount target.' },
|
|
34
|
+
{ component: 'SingleLayout', category: 'chrome', renderer: { kind: 'function', render: SingleLayout }, description: 'Only the active mount.' },
|
|
35
|
+
{ component: 'TabsLayout', category: 'chrome', renderer: { kind: 'function', render: TabsLayout }, description: 'Tab strip plus the active mount.' },
|
|
36
|
+
{ component: 'StackLayout', category: 'chrome', renderer: { kind: 'function', render: StackLayout }, description: 'All mounts, stacked with headings.' },
|
|
37
|
+
{ component: 'SplitLayout', category: 'chrome', renderer: { kind: 'function', render: SplitLayout }, description: 'All mounts, side by side.' },
|
|
38
|
+
{ component: 'BarLayout', category: 'chrome', renderer: { kind: 'function', render: BarLayout }, description: 'All mounts on one line.' },
|
|
39
|
+
{ component: 'RailLayout', category: 'chrome', renderer: { kind: 'function', render: RailLayout }, description: 'Icons only.' },
|
|
40
|
+
{ component: 'InlineLayout', category: 'chrome', renderer: { kind: 'function', render: InlineLayout }, description: 'All mounts, no chrome.' },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export const BUILTIN_LAYOUTS = [
|
|
44
|
+
{ name: 'single' as const, component: 'SingleLayout', title: 'Single' },
|
|
45
|
+
{ name: 'tabs' as const, component: 'TabsLayout', title: 'Tabs' },
|
|
46
|
+
{ name: 'stack' as const, component: 'StackLayout', title: 'Stack' },
|
|
47
|
+
{ name: 'split' as const, component: 'SplitLayout', title: 'Split' },
|
|
48
|
+
{ name: 'bar' as const, component: 'BarLayout', title: 'Bar' },
|
|
49
|
+
{ name: 'rail' as const, component: 'RailLayout', title: 'Rail' },
|
|
50
|
+
{ name: 'inline' as const, component: 'InlineLayout', title: 'Inline' },
|
|
51
|
+
{ name: 'floating' as const, component: 'InlineLayout', title: 'Floating' },
|
|
52
|
+
{ name: 'toast' as const, component: 'StackLayout', title: 'Toast' },
|
|
53
|
+
];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineComponent, h } from '@textui/core';
|
|
2
|
+
import { MountView } from './mount-view.js';
|
|
3
|
+
import type { LayoutProps } from './shared.js';
|
|
4
|
+
|
|
5
|
+
/** Every mount, no chrome at all. */
|
|
6
|
+
export const InlineLayout = defineComponent<LayoutProps>('InlineLayout', (props) => {
|
|
7
|
+
const { mounts, surface: _surface, state: _state, ...rest } = props;
|
|
8
|
+
return h('box', { direction: 'column', flex: 1, ...rest },
|
|
9
|
+
...mounts.map((mount) => h(MountView, { key: mount.key, mount })),
|
|
10
|
+
);
|
|
11
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { BindingPath, BoxProps, ComponentNode, Mount } from '@textui/core';
|
|
2
|
+
import { defineComponent, h, useRuntime } from '@textui/core';
|
|
3
|
+
import { EmptyState } from '../display/index.js';
|
|
4
|
+
|
|
5
|
+
export interface MountViewProps extends BoxProps {
|
|
6
|
+
mount: Mount;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Renders one mount's target - an inline node, or a node read from a path. */
|
|
10
|
+
export const MountView = defineComponent<MountViewProps>('MountView', ({ mount, ...rest }) => {
|
|
11
|
+
const runtime = useRuntime();
|
|
12
|
+
|
|
13
|
+
const target = mount.target;
|
|
14
|
+
const node: ComponentNode | null =
|
|
15
|
+
'component' in target
|
|
16
|
+
? target
|
|
17
|
+
: (runtime.store.get<ComponentNode>((target as { path: BindingPath }).path) ?? null);
|
|
18
|
+
|
|
19
|
+
if (!node) {
|
|
20
|
+
return h(EmptyState, { title: 'Nothing mounted', ...rest });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// A mount's data context makes relative paths inside it resolve against the
|
|
24
|
+
// record it was opened for.
|
|
25
|
+
const withContext = mount.dataContext
|
|
26
|
+
? { ...node, dataContext: mount.dataContext }
|
|
27
|
+
: node;
|
|
28
|
+
|
|
29
|
+
return h('box', { flex: 1, direction: 'column', ...rest }, withContext);
|
|
30
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineComponent, h, useRuntime, useTheme } from '@textui/core';
|
|
2
|
+
import type { LayoutProps } from './shared.js';
|
|
3
|
+
|
|
4
|
+
/** Icons only, one per mount. An activity bar. */
|
|
5
|
+
export const RailLayout = defineComponent<LayoutProps>('RailLayout', (props) => {
|
|
6
|
+
const runtime = useRuntime();
|
|
7
|
+
const theme = useTheme();
|
|
8
|
+
const { mounts, state, surface, ...rest } = props;
|
|
9
|
+
const app = runtime.app();
|
|
10
|
+
|
|
11
|
+
return h('box', { direction: 'column', width: 3, align: 'center', ...rest },
|
|
12
|
+
...mounts.map((mount) => {
|
|
13
|
+
const active = mount.key === state.activeKey;
|
|
14
|
+
return h('box', {
|
|
15
|
+
key: mount.key,
|
|
16
|
+
direction: 'row',
|
|
17
|
+
gap: 0,
|
|
18
|
+
fg: active ? 'accent' : 'muted',
|
|
19
|
+
bold: active,
|
|
20
|
+
onClick: () => app?.surfaces.activate(surface, mount.key),
|
|
21
|
+
},
|
|
22
|
+
h('text', { content: active ? theme.glyphs.chevronRight : ' ' }),
|
|
23
|
+
h('text', { content: mount.display?.icon ?? mount.key.slice(0, 1).toUpperCase() }),
|
|
24
|
+
);
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineComponent, h } from '@textui/core';
|
|
2
|
+
import { MountView } from './mount-view.js';
|
|
3
|
+
import type { LayoutProps } from './shared.js';
|
|
4
|
+
|
|
5
|
+
/** Only the active mount. */
|
|
6
|
+
export const SingleLayout = defineComponent<LayoutProps>('SingleLayout', (props) => {
|
|
7
|
+
const { mounts, state, surface: _surface, ...rest } = props;
|
|
8
|
+
const active = mounts.find((m) => m.key === state.activeKey) ?? mounts[0];
|
|
9
|
+
if (!active) return null;
|
|
10
|
+
|
|
11
|
+
// A mount that declares a title gets one drawn, the same rule `stack`
|
|
12
|
+
// follows. It matters more here: `stack` shows everything at once, so which
|
|
13
|
+
// panel you are looking at is never a question, and this one shows one of
|
|
14
|
+
// several and answers it no other way.
|
|
15
|
+
if (active.display?.title === undefined) return h(MountView, { mount: active, ...rest });
|
|
16
|
+
return h('box', { direction: 'column', flex: 1, ...rest },
|
|
17
|
+
h('box', { direction: 'row', gap: 1 },
|
|
18
|
+
h('text', { content: active.display.title, bold: true, fg: 'muted' }),
|
|
19
|
+
active.display.badge !== undefined
|
|
20
|
+
? h('text', { content: String(active.display.badge), fg: 'subtle' })
|
|
21
|
+
: null,
|
|
22
|
+
h('spacer', { flex: 1 })),
|
|
23
|
+
h(MountView, { mount: active, flex: 1 }),
|
|
24
|
+
);
|
|
25
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineComponent, h } from '@textui/core';
|
|
2
|
+
import { MountView } from './mount-view.js';
|
|
3
|
+
import type { LayoutProps } from './shared.js';
|
|
4
|
+
|
|
5
|
+
/** Every mount, side by side, sharing the space. */
|
|
6
|
+
export const SplitLayout = defineComponent<LayoutProps>('SplitLayout', (props) => {
|
|
7
|
+
const { mounts, surface: _surface, state: _state, direction = 'row', ...rest } = props;
|
|
8
|
+
return h('box', { direction, flex: 1, gap: 1, ...rest },
|
|
9
|
+
...mounts.map((mount) => h(MountView, { key: mount.key, mount, flex: 1 })),
|
|
10
|
+
);
|
|
11
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { defineComponent, h, useTheme } from '@textui/core';
|
|
2
|
+
import { MountView } from './mount-view.js';
|
|
3
|
+
import type { LayoutProps } from './shared.js';
|
|
4
|
+
|
|
5
|
+
/** Every mount, stacked, each under its own heading. */
|
|
6
|
+
export const StackLayout = defineComponent<LayoutProps>('StackLayout', (props) => {
|
|
7
|
+
const theme = useTheme();
|
|
8
|
+
const { mounts, surface: _surface, state: _state, ...rest } = props;
|
|
9
|
+
|
|
10
|
+
return h('box', { direction: 'column', flex: 1, ...rest },
|
|
11
|
+
...mounts.map((mount, i) =>
|
|
12
|
+
// Each mount fills its share of the surface rather than only what its
|
|
13
|
+
// own content needs. A content-sized wrapper hands a viewport no height
|
|
14
|
+
// to measure, and a tree inside one reports a rect of 3 rows in a
|
|
15
|
+
// twenty-row sidebar - or of 0, once its content overflows.
|
|
16
|
+
h('box', { key: mount.key, direction: 'column', flex: 1 },
|
|
17
|
+
mount.display?.title
|
|
18
|
+
? h('box', { direction: 'row', gap: 1 },
|
|
19
|
+
h('text', { content: mount.display.title, bold: true, fg: 'muted' }),
|
|
20
|
+
mount.display.badge !== undefined
|
|
21
|
+
? h('text', { content: String(mount.display.badge), fg: 'subtle' })
|
|
22
|
+
: null,
|
|
23
|
+
h('spacer', { flex: 1 }))
|
|
24
|
+
: null,
|
|
25
|
+
h(MountView, { mount }),
|
|
26
|
+
// A rule separates two mounts. After the last one it separates a mount
|
|
27
|
+
// from nothing, which is a line under a sidebar with one panel in it.
|
|
28
|
+
i < mounts.length - 1
|
|
29
|
+
? h('box', { height: 1, fill: theme.borderChars().top, fg: 'borderSubtle' })
|
|
30
|
+
: null)),
|
|
31
|
+
);
|
|
32
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { BindingPath, BoxProps, ComponentNode, SurfaceName } from '@textui/core';
|
|
2
|
+
import { defineComponent, h, useRuntime, useStoreSubtree } from '@textui/core';
|
|
3
|
+
|
|
4
|
+
export interface SurfaceAreaProps extends BoxProps {
|
|
5
|
+
surface: SurfaceName;
|
|
6
|
+
/** Override the surface's stored layout. */
|
|
7
|
+
layout?: string;
|
|
8
|
+
/** Rendered when the surface has no visible mounts. */
|
|
9
|
+
fallback?: ComponentNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Render one surface through its active layout.
|
|
14
|
+
*
|
|
15
|
+
* This subscribes to the surface's state and mount list rather than to the
|
|
16
|
+
* whole store, so opening a tab in `main` does not re-render the status bar.
|
|
17
|
+
*/
|
|
18
|
+
export const SurfaceArea = defineComponent<SurfaceAreaProps>('SurfaceArea', (props) => {
|
|
19
|
+
const runtime = useRuntime();
|
|
20
|
+
const { surface, layout: layoutOverride, fallback, ...rest } = props;
|
|
21
|
+
|
|
22
|
+
// Subscribing to both paths is what makes a surface reactive.
|
|
23
|
+
useStoreSubtree(`$/layout/surfaces/${surface}` as BindingPath);
|
|
24
|
+
useStoreSubtree(`$/layout/mounts/${surface}` as BindingPath);
|
|
25
|
+
|
|
26
|
+
const app = runtime.app();
|
|
27
|
+
if (!app) return null;
|
|
28
|
+
|
|
29
|
+
const state = app.surfaces.state(surface);
|
|
30
|
+
if (!state.visible || state.collapsed) return null;
|
|
31
|
+
|
|
32
|
+
const mounts = app.surfaces.mounts(surface);
|
|
33
|
+
if (mounts.length === 0) return fallback ? h('box', { ...rest }, fallback) : null;
|
|
34
|
+
|
|
35
|
+
const layoutName = layoutOverride ?? state.layout;
|
|
36
|
+
const definition = app.layouts.get(layoutName);
|
|
37
|
+
|
|
38
|
+
if (!definition) {
|
|
39
|
+
return h('text', {
|
|
40
|
+
content: `[textui] no layout registered as "${layoutName}"`,
|
|
41
|
+
fg: 'danger',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return h(definition.component, { surface, mounts, state, ...rest });
|
|
46
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { defineComponent, h, useRuntime } from '@textui/core';
|
|
2
|
+
import { Tabs } from '../navigation/index.js';
|
|
3
|
+
import { MountView } from './mount-view.js';
|
|
4
|
+
import type { LayoutProps } from './shared.js';
|
|
5
|
+
|
|
6
|
+
/** A tab strip and the active mount. */
|
|
7
|
+
export const TabsLayout = defineComponent<LayoutProps>('TabsLayout', (props) => {
|
|
8
|
+
const runtime = useRuntime();
|
|
9
|
+
const { mounts, state, surface, ...rest } = props;
|
|
10
|
+
if (mounts.length === 0) return null;
|
|
11
|
+
|
|
12
|
+
const active = mounts.find((m) => m.key === state.activeKey) ?? mounts[0];
|
|
13
|
+
const app = runtime.app();
|
|
14
|
+
|
|
15
|
+
return h('box', { direction: 'column', flex: 1, ...rest },
|
|
16
|
+
mounts.length > 1
|
|
17
|
+
? h(Tabs, {
|
|
18
|
+
items: mounts.map((m) => ({
|
|
19
|
+
id: m.key,
|
|
20
|
+
label: m.display?.title ?? m.key,
|
|
21
|
+
icon: m.display?.icon,
|
|
22
|
+
badge: m.display?.badge,
|
|
23
|
+
})),
|
|
24
|
+
activeId: active?.key,
|
|
25
|
+
onChange: (key: string) => app?.surfaces.activate(surface, key),
|
|
26
|
+
})
|
|
27
|
+
: null,
|
|
28
|
+
active ? h(MountView, { mount: active, flex: 1 }) : null,
|
|
29
|
+
);
|
|
30
|
+
});
|
package/src/tone.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SemanticVariant, StyleColor } from '@textui/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The two halves of a tone.
|
|
5
|
+
*
|
|
6
|
+
* `TONE` is the colour a tone is drawn *in*; `ON_TONE` is what is written *on*
|
|
7
|
+
* it once it becomes a background. They belong together and are stated once,
|
|
8
|
+
* because a component that pairs `danger` with the wrong foreground produces
|
|
9
|
+
* the one thing a status colour must never be, which is unreadable.
|
|
10
|
+
*
|
|
11
|
+
* The theme states an `on*` token per tone rather than a single "inverted" for
|
|
12
|
+
* all of them: the contrast that works on a green fill is not the one that
|
|
13
|
+
* works on a red one.
|
|
14
|
+
*/
|
|
15
|
+
export const TONE: Record<SemanticVariant, StyleColor> = {
|
|
16
|
+
default: 'text',
|
|
17
|
+
primary: 'primary',
|
|
18
|
+
secondary: 'secondary',
|
|
19
|
+
accent: 'accent',
|
|
20
|
+
success: 'success',
|
|
21
|
+
warning: 'warning',
|
|
22
|
+
danger: 'danger',
|
|
23
|
+
info: 'info',
|
|
24
|
+
muted: 'muted',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Adding a tone, or renaming an `on*` token, means three edits and only two of
|
|
28
|
+
// them are checked:
|
|
29
|
+
//
|
|
30
|
+
// 1. this map and its `TONE` pair;
|
|
31
|
+
// 2. every theme in themes/builtin.ts, or the token resolves to nothing;
|
|
32
|
+
// 3. the palette list in docs/themes/tokens.md - which `pnpm docs:check`
|
|
33
|
+
// does NOT catch, because that check reads component prop interfaces and
|
|
34
|
+
// knows nothing about token names.
|
|
35
|
+
//
|
|
36
|
+
// The third is the one that gets missed. `onDefault`, `onSecondary` and
|
|
37
|
+
// `onMuted` were absent from that page until they were added by hand.
|
|
38
|
+
export const ON_TONE: Record<SemanticVariant, StyleColor> = {
|
|
39
|
+
default: 'onDefault',
|
|
40
|
+
primary: 'onPrimary',
|
|
41
|
+
secondary: 'onSecondary',
|
|
42
|
+
accent: 'onAccent',
|
|
43
|
+
success: 'onSuccess',
|
|
44
|
+
warning: 'onWarning',
|
|
45
|
+
danger: 'onDanger',
|
|
46
|
+
info: 'onInfo',
|
|
47
|
+
muted: 'onMuted',
|
|
48
|
+
};
|
package/src/viewport.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Rect, Dimension } from '@textui/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* How many rows a scrolling component should draw.
|
|
5
|
+
*
|
|
6
|
+
* The subtle part is knowing when the measurement means anything. A box that
|
|
7
|
+
* was stretched, flexed or given a height fills what the layout handed it, and
|
|
8
|
+
* measuring tells it how much that was. A box with none of those is sized *by*
|
|
9
|
+
* its own content - measuring it just reports back how many rows it drew last
|
|
10
|
+
* frame, and clamping to that would mean an expanding tree could never grow
|
|
11
|
+
* past the size it had when it was first drawn.
|
|
12
|
+
*
|
|
13
|
+
* So: layout-sized components fill and scroll, content-sized ones draw
|
|
14
|
+
* everything, and which one you get is decided by the props you passed.
|
|
15
|
+
*/
|
|
16
|
+
export interface SizedProps {
|
|
17
|
+
flex?: number;
|
|
18
|
+
height?: Dimension;
|
|
19
|
+
maxHeight?: number;
|
|
20
|
+
basis?: Dimension;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function sizedByLayout(props: SizedProps): boolean {
|
|
24
|
+
return (
|
|
25
|
+
props.flex !== undefined ||
|
|
26
|
+
props.height !== undefined ||
|
|
27
|
+
props.maxHeight !== undefined ||
|
|
28
|
+
props.basis !== undefined
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The row count to render: what was asked for, else what fits, else all of it.
|
|
34
|
+
* `reserved` takes off rows the component spends on chrome, like a header.
|
|
35
|
+
*/
|
|
36
|
+
export function viewportRows(
|
|
37
|
+
props: SizedProps,
|
|
38
|
+
measured: Rect,
|
|
39
|
+
content: number,
|
|
40
|
+
options: { requested?: number; reserved?: number } = {},
|
|
41
|
+
): number {
|
|
42
|
+
if (options.requested !== undefined) return Math.max(1, options.requested);
|
|
43
|
+
if (!sizedByLayout(props) || measured.height <= 0) return content;
|
|
44
|
+
return Math.max(1, measured.height - (options.reserved ?? 0));
|
|
45
|
+
}
|