@snap-rail/framework-shadcn 0.2.0 → 0.2.2
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 +3 -2
- package/lib/fallback.js +10 -2
- package/lib/index.js +43 -5
- package/lib/runtime.js +43 -5
- package/lib/semantic.js +10 -2
- package/lib/slots.js +10 -2
- package/lib/types/runtime.d.ts +14 -0
- package/lib/types/runtime.js +11 -5
- package/lib/types/slots.d.ts +8 -0
- package/lib/types/slots.js +8 -0
- package/lib/types/surface-shell.d.ts +21 -0
- package/lib/types/surface-shell.js +17 -0
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
界面框架插件(clientOnly、单活 `snapRail.exclusive: 'framework'`):槽位词汇、
|
|
4
4
|
renderer runtime + Shell、shadcn 风格共享原语、主题令牌(预编译 tailwind 输出
|
|
5
|
-
随 zip 内联)、settings/session/variables/workflows
|
|
5
|
+
随 zip 内联)、settings/session/variables/workflows 服务、fallback 布局与辅助
|
|
6
|
+
surface 壳(`surface-shell.tsx` 渲染 `ball` 悬浮球面)。
|
|
6
7
|
|
|
7
|
-
组件插件经 `requires: ['@snap-rail/framework-shadcn@^0.
|
|
8
|
+
组件插件经 `requires: ['@snap-rail/framework-shadcn@^0.2']` 绑定本框架(绑定即依赖);框架不匹配装不上。
|
|
8
9
|
构建链:tsc(lib/types)→ tailwind(lib-client/theme.css)→ tsdown(lib 面 +
|
|
9
10
|
clientBundle 工厂包)。
|
|
10
11
|
|
package/lib/fallback.js
CHANGED
|
@@ -27,7 +27,14 @@ const UI_SLOT_IDS = {
|
|
|
27
27
|
main: "shell.main",
|
|
28
28
|
view: "shell.view",
|
|
29
29
|
statusbar: "shell.statusbar",
|
|
30
|
-
overlay: "shell.overlay"
|
|
30
|
+
overlay: "shell.overlay",
|
|
31
|
+
/**
|
|
32
|
+
* The floating ball (`shell.ball`): the auxiliary-surface face. A package
|
|
33
|
+
* that ships a `ball` client bundle occupies it; when nobody does, the
|
|
34
|
+
* surface renders the framework's default (an orb that summons the operator
|
|
35
|
+
* window). `single`, like every other "one tenant paints this window" slot.
|
|
36
|
+
*/
|
|
37
|
+
ball: "shell.ball"
|
|
31
38
|
};
|
|
32
39
|
new Set(Object.values(UI_SLOT_IDS));
|
|
33
40
|
/**
|
|
@@ -48,7 +55,8 @@ const UI_SLOT_MODES = {
|
|
|
48
55
|
[UI_SLOT_IDS.main]: "single",
|
|
49
56
|
[UI_SLOT_IDS.view]: "stack",
|
|
50
57
|
[UI_SLOT_IDS.statusbar]: "stack",
|
|
51
|
-
[UI_SLOT_IDS.overlay]: "stack"
|
|
58
|
+
[UI_SLOT_IDS.overlay]: "stack",
|
|
59
|
+
[UI_SLOT_IDS.ball]: "single"
|
|
52
60
|
};
|
|
53
61
|
new Map(Object.entries(UI_SLOT_MODES));
|
|
54
62
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { SESSION_OPERATOR_KEY, settingsChangedSchema } from "@snap-rail/station-
|
|
|
22
22
|
import { PLUGINS_FEED_URLS_KEY, UPDATE_AUTO_CHECK_KEY, UPDATE_FEED_URL_KEY, pluginUpdatesChangedSchema, updateStatusSchema } from "@snap-rail/app-boot/contract";
|
|
23
23
|
import { equipmentFrameSchemas, pointKey } from "@snap-rail/equipment/contract";
|
|
24
24
|
import { createRoot } from "react-dom/client";
|
|
25
|
+
import { SurfaceFallback } from "@snap-rail/client-kernel";
|
|
25
26
|
//#region lib/types/settings-pages.js
|
|
26
27
|
/**
|
|
27
28
|
* Mounts the `ctx.settingsPages` registry. All state lives in
|
|
@@ -99,7 +100,14 @@ const UI_SLOT_IDS = {
|
|
|
99
100
|
main: "shell.main",
|
|
100
101
|
view: "shell.view",
|
|
101
102
|
statusbar: "shell.statusbar",
|
|
102
|
-
overlay: "shell.overlay"
|
|
103
|
+
overlay: "shell.overlay",
|
|
104
|
+
/**
|
|
105
|
+
* The floating ball (`shell.ball`): the auxiliary-surface face. A package
|
|
106
|
+
* that ships a `ball` client bundle occupies it; when nobody does, the
|
|
107
|
+
* surface renders the framework's default (an orb that summons the operator
|
|
108
|
+
* window). `single`, like every other "one tenant paints this window" slot.
|
|
109
|
+
*/
|
|
110
|
+
ball: "shell.ball"
|
|
103
111
|
};
|
|
104
112
|
/** Runtime set used by manifest/semantic validation as well as TypeScript. */
|
|
105
113
|
const STANDARD_UI_SLOT_IDS = new Set(Object.values(UI_SLOT_IDS));
|
|
@@ -127,7 +135,8 @@ const UI_SLOT_MODES = {
|
|
|
127
135
|
[UI_SLOT_IDS.main]: "single",
|
|
128
136
|
[UI_SLOT_IDS.view]: "stack",
|
|
129
137
|
[UI_SLOT_IDS.statusbar]: "stack",
|
|
130
|
-
[UI_SLOT_IDS.overlay]: "stack"
|
|
138
|
+
[UI_SLOT_IDS.overlay]: "stack",
|
|
139
|
+
[UI_SLOT_IDS.ball]: "single"
|
|
131
140
|
};
|
|
132
141
|
const STANDARD_SLOT_MODES = new Map(Object.entries(UI_SLOT_MODES));
|
|
133
142
|
/** The occupancy mode of a slot; `custom:*` slots are `stack`. */
|
|
@@ -2988,6 +2997,28 @@ function Shell(props) {
|
|
|
2988
2997
|
});
|
|
2989
2998
|
}
|
|
2990
2999
|
//#endregion
|
|
3000
|
+
//#region lib/types/surface-shell.js
|
|
3001
|
+
/** Shell root for the auxiliary window; mounted by `createClientRuntime`. */
|
|
3002
|
+
function SurfaceShell(props) {
|
|
3003
|
+
const [tick, setTick] = useState(0);
|
|
3004
|
+
useEffect(() => {
|
|
3005
|
+
const detach = props.ctx.on("ui/slot-changed", () => setTick((value) => value + 1));
|
|
3006
|
+
return () => {
|
|
3007
|
+
detach();
|
|
3008
|
+
};
|
|
3009
|
+
}, [props.ctx]);
|
|
3010
|
+
const occupant = props.ctx.uiSlots.list(UI_SLOT_IDS.ball)[0];
|
|
3011
|
+
return jsx("div", {
|
|
3012
|
+
className: "h-full w-full",
|
|
3013
|
+
"data-tick": tick,
|
|
3014
|
+
"data-surface": props.surface,
|
|
3015
|
+
children: occupant === void 0 ? jsx(SurfaceFallback, {
|
|
3016
|
+
link: props.ctx.client.link,
|
|
3017
|
+
surface: props.surface
|
|
3018
|
+
}) : occupant.render()
|
|
3019
|
+
});
|
|
3020
|
+
}
|
|
3021
|
+
//#endregion
|
|
2991
3022
|
//#region lib/types/runtime.js
|
|
2992
3023
|
/**
|
|
2993
3024
|
* The client renderer host: takes over the React root from the kernel,
|
|
@@ -3006,8 +3037,12 @@ function Shell(props) {
|
|
|
3006
3037
|
* @param options - see {@link RuntimeOptions}.
|
|
3007
3038
|
*/
|
|
3008
3039
|
async function createClientRuntime(handle, options) {
|
|
3040
|
+
const surface = options.surface ?? "main";
|
|
3009
3041
|
const ctx = new Context();
|
|
3010
|
-
ctx.provide("client", {
|
|
3042
|
+
ctx.provide("client", {
|
|
3043
|
+
link: handle.link,
|
|
3044
|
+
surface
|
|
3045
|
+
});
|
|
3011
3046
|
await ctx.plugin(TimerService);
|
|
3012
3047
|
await ctx.plugin(slotsPlugin);
|
|
3013
3048
|
await ctx.plugin(semanticPlugin);
|
|
@@ -3021,10 +3056,13 @@ async function createClientRuntime(handle, options) {
|
|
|
3021
3056
|
const plugin = spec.plugin;
|
|
3022
3057
|
await ctx.plugin(plugin, spec.config === void 0 ? {} : spec.config);
|
|
3023
3058
|
}
|
|
3024
|
-
await ctx.plugin(fallbackShellPlugin);
|
|
3059
|
+
if (surface === "main") await ctx.plugin(fallbackShellPlugin);
|
|
3025
3060
|
handle.root.unmount();
|
|
3026
3061
|
const shellRoot = createRoot(handle.element);
|
|
3027
|
-
shellRoot.render(jsx(Shell, { ctx })
|
|
3062
|
+
shellRoot.render(surface === "main" ? jsx(Shell, { ctx }) : jsx(SurfaceShell, {
|
|
3063
|
+
ctx,
|
|
3064
|
+
surface
|
|
3065
|
+
}));
|
|
3028
3066
|
return {
|
|
3029
3067
|
ctx,
|
|
3030
3068
|
dispose: () => ctx.fiber.dispose().then(() => {
|
package/lib/runtime.js
CHANGED
|
@@ -22,6 +22,7 @@ import { SESSION_OPERATOR_KEY, settingsChangedSchema } from "@snap-rail/station-
|
|
|
22
22
|
import { PLUGINS_FEED_URLS_KEY, UPDATE_AUTO_CHECK_KEY, UPDATE_FEED_URL_KEY, pluginUpdatesChangedSchema, updateStatusSchema } from "@snap-rail/app-boot/contract";
|
|
23
23
|
import { pointKey } from "@snap-rail/equipment/contract";
|
|
24
24
|
import { createRoot } from "react-dom/client";
|
|
25
|
+
import { SurfaceFallback } from "@snap-rail/client-kernel";
|
|
25
26
|
//#region lib/types/settings-pages.js
|
|
26
27
|
/**
|
|
27
28
|
* Mounts the `ctx.settingsPages` registry. All state lives in
|
|
@@ -99,7 +100,14 @@ const UI_SLOT_IDS = {
|
|
|
99
100
|
main: "shell.main",
|
|
100
101
|
view: "shell.view",
|
|
101
102
|
statusbar: "shell.statusbar",
|
|
102
|
-
overlay: "shell.overlay"
|
|
103
|
+
overlay: "shell.overlay",
|
|
104
|
+
/**
|
|
105
|
+
* The floating ball (`shell.ball`): the auxiliary-surface face. A package
|
|
106
|
+
* that ships a `ball` client bundle occupies it; when nobody does, the
|
|
107
|
+
* surface renders the framework's default (an orb that summons the operator
|
|
108
|
+
* window). `single`, like every other "one tenant paints this window" slot.
|
|
109
|
+
*/
|
|
110
|
+
ball: "shell.ball"
|
|
103
111
|
};
|
|
104
112
|
/** Runtime set used by manifest/semantic validation as well as TypeScript. */
|
|
105
113
|
const STANDARD_UI_SLOT_IDS = new Set(Object.values(UI_SLOT_IDS));
|
|
@@ -127,7 +135,8 @@ const UI_SLOT_MODES = {
|
|
|
127
135
|
[UI_SLOT_IDS.main]: "single",
|
|
128
136
|
[UI_SLOT_IDS.view]: "stack",
|
|
129
137
|
[UI_SLOT_IDS.statusbar]: "stack",
|
|
130
|
-
[UI_SLOT_IDS.overlay]: "stack"
|
|
138
|
+
[UI_SLOT_IDS.overlay]: "stack",
|
|
139
|
+
[UI_SLOT_IDS.ball]: "single"
|
|
131
140
|
};
|
|
132
141
|
const STANDARD_SLOT_MODES = new Map(Object.entries(UI_SLOT_MODES));
|
|
133
142
|
/** The occupancy mode of a slot; `custom:*` slots are `stack`. */
|
|
@@ -2779,6 +2788,28 @@ function Shell(props) {
|
|
|
2779
2788
|
});
|
|
2780
2789
|
}
|
|
2781
2790
|
//#endregion
|
|
2791
|
+
//#region lib/types/surface-shell.js
|
|
2792
|
+
/** Shell root for the auxiliary window; mounted by `createClientRuntime`. */
|
|
2793
|
+
function SurfaceShell(props) {
|
|
2794
|
+
const [tick, setTick] = useState(0);
|
|
2795
|
+
useEffect(() => {
|
|
2796
|
+
const detach = props.ctx.on("ui/slot-changed", () => setTick((value) => value + 1));
|
|
2797
|
+
return () => {
|
|
2798
|
+
detach();
|
|
2799
|
+
};
|
|
2800
|
+
}, [props.ctx]);
|
|
2801
|
+
const occupant = props.ctx.uiSlots.list(UI_SLOT_IDS.ball)[0];
|
|
2802
|
+
return jsx("div", {
|
|
2803
|
+
className: "h-full w-full",
|
|
2804
|
+
"data-tick": tick,
|
|
2805
|
+
"data-surface": props.surface,
|
|
2806
|
+
children: occupant === void 0 ? jsx(SurfaceFallback, {
|
|
2807
|
+
link: props.ctx.client.link,
|
|
2808
|
+
surface: props.surface
|
|
2809
|
+
}) : occupant.render()
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
//#endregion
|
|
2782
2813
|
//#region lib/types/runtime.js
|
|
2783
2814
|
/**
|
|
2784
2815
|
* The client renderer host: takes over the React root from the kernel,
|
|
@@ -2797,8 +2828,12 @@ function Shell(props) {
|
|
|
2797
2828
|
* @param options - see {@link RuntimeOptions}.
|
|
2798
2829
|
*/
|
|
2799
2830
|
async function createClientRuntime(handle, options) {
|
|
2831
|
+
const surface = options.surface ?? "main";
|
|
2800
2832
|
const ctx = new Context();
|
|
2801
|
-
ctx.provide("client", {
|
|
2833
|
+
ctx.provide("client", {
|
|
2834
|
+
link: handle.link,
|
|
2835
|
+
surface
|
|
2836
|
+
});
|
|
2802
2837
|
await ctx.plugin(TimerService);
|
|
2803
2838
|
await ctx.plugin(slotsPlugin);
|
|
2804
2839
|
await ctx.plugin(semanticPlugin);
|
|
@@ -2812,10 +2847,13 @@ async function createClientRuntime(handle, options) {
|
|
|
2812
2847
|
const plugin = spec.plugin;
|
|
2813
2848
|
await ctx.plugin(plugin, spec.config === void 0 ? {} : spec.config);
|
|
2814
2849
|
}
|
|
2815
|
-
await ctx.plugin(fallbackShellPlugin);
|
|
2850
|
+
if (surface === "main") await ctx.plugin(fallbackShellPlugin);
|
|
2816
2851
|
handle.root.unmount();
|
|
2817
2852
|
const shellRoot = createRoot(handle.element);
|
|
2818
|
-
shellRoot.render(jsx(Shell, { ctx })
|
|
2853
|
+
shellRoot.render(surface === "main" ? jsx(Shell, { ctx }) : jsx(SurfaceShell, {
|
|
2854
|
+
ctx,
|
|
2855
|
+
surface
|
|
2856
|
+
}));
|
|
2819
2857
|
return {
|
|
2820
2858
|
ctx,
|
|
2821
2859
|
dispose: () => ctx.fiber.dispose().then(() => {
|
package/lib/semantic.js
CHANGED
|
@@ -10,7 +10,14 @@ const UI_SLOT_IDS = {
|
|
|
10
10
|
main: "shell.main",
|
|
11
11
|
view: "shell.view",
|
|
12
12
|
statusbar: "shell.statusbar",
|
|
13
|
-
overlay: "shell.overlay"
|
|
13
|
+
overlay: "shell.overlay",
|
|
14
|
+
/**
|
|
15
|
+
* The floating ball (`shell.ball`): the auxiliary-surface face. A package
|
|
16
|
+
* that ships a `ball` client bundle occupies it; when nobody does, the
|
|
17
|
+
* surface renders the framework's default (an orb that summons the operator
|
|
18
|
+
* window). `single`, like every other "one tenant paints this window" slot.
|
|
19
|
+
*/
|
|
20
|
+
ball: "shell.ball"
|
|
14
21
|
};
|
|
15
22
|
/** Runtime set used by manifest/semantic validation as well as TypeScript. */
|
|
16
23
|
const STANDARD_UI_SLOT_IDS = new Set(Object.values(UI_SLOT_IDS));
|
|
@@ -38,7 +45,8 @@ const UI_SLOT_MODES = {
|
|
|
38
45
|
[UI_SLOT_IDS.main]: "single",
|
|
39
46
|
[UI_SLOT_IDS.view]: "stack",
|
|
40
47
|
[UI_SLOT_IDS.statusbar]: "stack",
|
|
41
|
-
[UI_SLOT_IDS.overlay]: "stack"
|
|
48
|
+
[UI_SLOT_IDS.overlay]: "stack",
|
|
49
|
+
[UI_SLOT_IDS.ball]: "single"
|
|
42
50
|
};
|
|
43
51
|
new Map(Object.entries(UI_SLOT_MODES));
|
|
44
52
|
//#endregion
|
package/lib/slots.js
CHANGED
|
@@ -10,7 +10,14 @@ const UI_SLOT_IDS = {
|
|
|
10
10
|
main: "shell.main",
|
|
11
11
|
view: "shell.view",
|
|
12
12
|
statusbar: "shell.statusbar",
|
|
13
|
-
overlay: "shell.overlay"
|
|
13
|
+
overlay: "shell.overlay",
|
|
14
|
+
/**
|
|
15
|
+
* The floating ball (`shell.ball`): the auxiliary-surface face. A package
|
|
16
|
+
* that ships a `ball` client bundle occupies it; when nobody does, the
|
|
17
|
+
* surface renders the framework's default (an orb that summons the operator
|
|
18
|
+
* window). `single`, like every other "one tenant paints this window" slot.
|
|
19
|
+
*/
|
|
20
|
+
ball: "shell.ball"
|
|
14
21
|
};
|
|
15
22
|
/** Runtime set used by manifest/semantic validation as well as TypeScript. */
|
|
16
23
|
const STANDARD_UI_SLOT_IDS = new Set(Object.values(UI_SLOT_IDS));
|
|
@@ -38,7 +45,8 @@ const UI_SLOT_MODES = {
|
|
|
38
45
|
[UI_SLOT_IDS.main]: "single",
|
|
39
46
|
[UI_SLOT_IDS.view]: "stack",
|
|
40
47
|
[UI_SLOT_IDS.statusbar]: "stack",
|
|
41
|
-
[UI_SLOT_IDS.overlay]: "stack"
|
|
48
|
+
[UI_SLOT_IDS.overlay]: "stack",
|
|
49
|
+
[UI_SLOT_IDS.ball]: "single"
|
|
42
50
|
};
|
|
43
51
|
const STANDARD_SLOT_MODES = new Map(Object.entries(UI_SLOT_MODES));
|
|
44
52
|
/** The occupancy mode of a slot; `custom:*` slots are `stack`. */
|
package/lib/types/runtime.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { Context, type Plugin } from '@snap-rail/cordis';
|
|
11
11
|
import type { ClientHandle } from '@snap-rail/client-kernel';
|
|
12
12
|
import type { HostLink } from '@snap-rail/connection';
|
|
13
|
+
import type { SurfaceId } from '@snap-rail/protocol';
|
|
13
14
|
export type { ClientHandle } from '@snap-rail/client-kernel';
|
|
14
15
|
declare module '@snap-rail/cordis' {
|
|
15
16
|
interface Context {
|
|
@@ -21,6 +22,12 @@ declare module '@snap-rail/cordis' {
|
|
|
21
22
|
export interface ClientServices {
|
|
22
23
|
/** The typed host link shared by every occupant. */
|
|
23
24
|
link: HostLink;
|
|
25
|
+
/**
|
|
26
|
+
* Which window this client tree serves (`main` or `ball`). A face bound to
|
|
27
|
+
* the auxiliary surface reads it to pick its compact layout, and a face
|
|
28
|
+
* that only makes sense on `main` can refuse to mount there.
|
|
29
|
+
*/
|
|
30
|
+
surface: SurfaceId;
|
|
24
31
|
}
|
|
25
32
|
/** One occupant seat: the plugin plus the config carved from its plugins.yml row. */
|
|
26
33
|
export interface OccupantSpec {
|
|
@@ -38,6 +45,13 @@ export interface RuntimeOptions {
|
|
|
38
45
|
* outside the bundle.
|
|
39
46
|
*/
|
|
40
47
|
plugins: readonly (Plugin | OccupantSpec)[];
|
|
48
|
+
/**
|
|
49
|
+
* The window this runtime serves; defaults to `main`. The auxiliary surface
|
|
50
|
+
* gets the same spine services but its own shell: one slot paints the whole
|
|
51
|
+
* window (`shell.ball`), and the operator shell — layouts, titlebar,
|
|
52
|
+
* views — is never mounted into a 128px orb.
|
|
53
|
+
*/
|
|
54
|
+
surface?: SurfaceId | undefined;
|
|
41
55
|
}
|
|
42
56
|
export interface RuntimeHandle {
|
|
43
57
|
ctx: Context;
|
package/lib/types/runtime.js
CHANGED
|
@@ -26,6 +26,7 @@ import variablesPlugin from "./variables.js";
|
|
|
26
26
|
import workflowsPlugin from "./workflows.js";
|
|
27
27
|
import { createRoot } from 'react-dom/client';
|
|
28
28
|
import { Shell } from "./Shell.js";
|
|
29
|
+
import { SurfaceShell } from "./surface-shell.js";
|
|
29
30
|
/**
|
|
30
31
|
* Take over the booted root: stop the startup page, mount the client plugin
|
|
31
32
|
* tree, render the shell.
|
|
@@ -34,10 +35,11 @@ import { Shell } from "./Shell.js";
|
|
|
34
35
|
* @param options - see {@link RuntimeOptions}.
|
|
35
36
|
*/
|
|
36
37
|
export async function createClientRuntime(handle, options) {
|
|
38
|
+
const surface = options.surface ?? 'main';
|
|
37
39
|
const ctx = new Context();
|
|
38
|
-
ctx.provide('client', { link: handle.link });
|
|
40
|
+
ctx.provide('client', { link: handle.link, surface });
|
|
39
41
|
// Spine services first: timer gives occupants disposable intervals, slots
|
|
40
|
-
// carries the
|
|
42
|
+
// carries the occupant seam, session and workflows sit under every occupant,
|
|
41
43
|
// and settingsPages/variables are the seams dialog pages and demand
|
|
42
44
|
// plugins consume. The settings shell rides right after its seam — the
|
|
43
45
|
// dialog, the theme adopter, and the first-party plugin/theme/update
|
|
@@ -60,12 +62,16 @@ export async function createClientRuntime(handle, options) {
|
|
|
60
62
|
}
|
|
61
63
|
// The framework's own fallback layout rides last: it renders only while it
|
|
62
64
|
// is the sole full-screen layout occupant and yields the moment a suite
|
|
63
|
-
// layout registers.
|
|
64
|
-
|
|
65
|
+
// layout registers. It is an operator-window concern — an auxiliary surface
|
|
66
|
+
// paints its own slot and has no layout to fall back from.
|
|
67
|
+
if (surface === 'main')
|
|
68
|
+
await ctx.plugin(fallbackPlugin);
|
|
65
69
|
// Take over the element: the startup root unmounts, a fresh shell roots.
|
|
66
70
|
handle.root.unmount();
|
|
67
71
|
const shellRoot = createRoot(handle.element);
|
|
68
|
-
shellRoot.render(
|
|
72
|
+
shellRoot.render(surface === 'main'
|
|
73
|
+
? _jsx(Shell, { ctx: ctx })
|
|
74
|
+
: _jsx(SurfaceShell, { ctx: ctx, surface: surface }));
|
|
69
75
|
return {
|
|
70
76
|
ctx,
|
|
71
77
|
dispose: () => ctx.fiber.dispose().then(() => {
|
package/lib/types/slots.d.ts
CHANGED
|
@@ -30,6 +30,13 @@ export declare const UI_SLOT_IDS: {
|
|
|
30
30
|
readonly view: "shell.view";
|
|
31
31
|
readonly statusbar: "shell.statusbar";
|
|
32
32
|
readonly overlay: "shell.overlay";
|
|
33
|
+
/**
|
|
34
|
+
* The floating ball (`shell.ball`): the auxiliary-surface face. A package
|
|
35
|
+
* that ships a `ball` client bundle occupies it; when nobody does, the
|
|
36
|
+
* surface renders the framework's default (an orb that summons the operator
|
|
37
|
+
* window). `single`, like every other "one tenant paints this window" slot.
|
|
38
|
+
*/
|
|
39
|
+
readonly ball: "shell.ball";
|
|
33
40
|
};
|
|
34
41
|
/** Runtime set used by manifest/semantic validation as well as TypeScript. */
|
|
35
42
|
export declare const STANDARD_UI_SLOT_IDS: ReadonlySet<string>;
|
|
@@ -60,6 +67,7 @@ export declare const UI_SLOT_MODES: {
|
|
|
60
67
|
readonly "shell.view": "stack";
|
|
61
68
|
readonly "shell.statusbar": "stack";
|
|
62
69
|
readonly "shell.overlay": "stack";
|
|
70
|
+
readonly "shell.ball": "single";
|
|
63
71
|
};
|
|
64
72
|
/** The occupancy mode of a slot; `custom:*` slots are `stack`. */
|
|
65
73
|
export declare function slotModeOf(slot: SlotId): UiSlotMode;
|
package/lib/types/slots.js
CHANGED
|
@@ -29,6 +29,13 @@ export const UI_SLOT_IDS = {
|
|
|
29
29
|
view: 'shell.view',
|
|
30
30
|
statusbar: 'shell.statusbar',
|
|
31
31
|
overlay: 'shell.overlay',
|
|
32
|
+
/**
|
|
33
|
+
* The floating ball (`shell.ball`): the auxiliary-surface face. A package
|
|
34
|
+
* that ships a `ball` client bundle occupies it; when nobody does, the
|
|
35
|
+
* surface renders the framework's default (an orb that summons the operator
|
|
36
|
+
* window). `single`, like every other "one tenant paints this window" slot.
|
|
37
|
+
*/
|
|
38
|
+
ball: 'shell.ball',
|
|
32
39
|
};
|
|
33
40
|
/** Runtime set used by manifest/semantic validation as well as TypeScript. */
|
|
34
41
|
export const STANDARD_UI_SLOT_IDS = new Set(Object.values(UI_SLOT_IDS));
|
|
@@ -57,6 +64,7 @@ export const UI_SLOT_MODES = {
|
|
|
57
64
|
[UI_SLOT_IDS.view]: 'stack',
|
|
58
65
|
[UI_SLOT_IDS.statusbar]: 'stack',
|
|
59
66
|
[UI_SLOT_IDS.overlay]: 'stack',
|
|
67
|
+
[UI_SLOT_IDS.ball]: 'single',
|
|
60
68
|
};
|
|
61
69
|
const STANDARD_SLOT_MODES = new Map(Object.entries(UI_SLOT_MODES));
|
|
62
70
|
/** The occupancy mode of a slot; `custom:*` slots are `stack`. */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auxiliary-surface shell: what the ball window renders instead of the
|
|
3
|
+
* operator shell. A 128px transparent window has no room for a titlebar,
|
|
4
|
+
* a sidebar, or a view port — mounting the operator shell there would paint a
|
|
5
|
+
* miniature copy of the terminal into a floating orb.
|
|
6
|
+
*
|
|
7
|
+
* One slot (`shell.ball`) and the elected occupant paints the whole window;
|
|
8
|
+
* with no occupant the framework's default takes over — the kernel's
|
|
9
|
+
* fallback, which is already the right shape for this window (a draggable orb
|
|
10
|
+
* that summons the operator window). The default is never destroyed, only
|
|
11
|
+
* shadowed, so unloading a suite falls back automatically.
|
|
12
|
+
*
|
|
13
|
+
* @module @snap-rail/framework-shadcn/surface-shell
|
|
14
|
+
*/
|
|
15
|
+
import type { Context } from '@snap-rail/cordis';
|
|
16
|
+
import { type ReactNode } from 'react';
|
|
17
|
+
/** Shell root for the auxiliary window; mounted by `createClientRuntime`. */
|
|
18
|
+
export declare function SurfaceShell(props: {
|
|
19
|
+
ctx: Context;
|
|
20
|
+
surface: 'ball';
|
|
21
|
+
}): ReactNode;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { SurfaceFallback } from '@snap-rail/client-kernel';
|
|
4
|
+
import { UI_SLOT_IDS } from "./slots.js";
|
|
5
|
+
/** Shell root for the auxiliary window; mounted by `createClientRuntime`. */
|
|
6
|
+
export function SurfaceShell(props) {
|
|
7
|
+
const [tick, setTick] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const detach = props.ctx.on('ui/slot-changed', () => setTick(value => value + 1));
|
|
10
|
+
return () => { detach(); };
|
|
11
|
+
}, [props.ctx]);
|
|
12
|
+
// `single` slots answer with the elected winner only, so index 0 is the tenant.
|
|
13
|
+
const occupant = props.ctx.uiSlots.list(UI_SLOT_IDS.ball)[0];
|
|
14
|
+
return (_jsx("div", { className: "h-full w-full", "data-tick": tick, "data-surface": props.surface, children: occupant === undefined
|
|
15
|
+
? _jsx(SurfaceFallback, { link: props.ctx.client.link, surface: props.surface })
|
|
16
|
+
: occupant.render() }));
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snap-rail/framework-shadcn",
|
|
3
3
|
"description": "The UI framework (single-active): slot vocabulary, renderer runtime + shell, shadcn-style shared primitives, theme tokens, settings/session/variables/workflows services, the settings dialog shell with its first-party plugin/theme/update pages, and the fallback layout",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"@snap-rail/cordis": "^4.0.1",
|
|
80
80
|
"@snap-rail/equipment": "^0.1.0",
|
|
81
|
-
"@snap-rail/protocol": "^0.1.
|
|
81
|
+
"@snap-rail/protocol": "^0.1.3",
|
|
82
82
|
"react": ">=19",
|
|
83
83
|
"react-dom": ">=19"
|
|
84
84
|
},
|
|
@@ -96,20 +96,20 @@
|
|
|
96
96
|
"tsdown": "^0.22.2",
|
|
97
97
|
"typescript": "^6.0.3",
|
|
98
98
|
"vitest": "^4.1.8",
|
|
99
|
-
"@snap-rail/app-boot": "0.1.
|
|
100
|
-
"@snap-rail/
|
|
101
|
-
"@snap-rail/client-kernel": "0.1.1",
|
|
102
|
-
"@snap-rail/connection": "0.1.0",
|
|
99
|
+
"@snap-rail/app-boot": "0.1.3",
|
|
100
|
+
"@snap-rail/client-kernel": "0.1.2",
|
|
103
101
|
"@snap-rail/cordis": "4.0.3",
|
|
104
102
|
"@snap-rail/cordis-plugin-timer": "1.1.3",
|
|
103
|
+
"@snap-rail/audit": "0.1.0",
|
|
105
104
|
"@snap-rail/equipment": "0.1.4",
|
|
106
105
|
"@snap-rail/gateway": "0.1.1",
|
|
107
|
-
"@snap-rail/plugin-kit": "0.2.
|
|
108
|
-
"@snap-rail/plugin-sdk": "0.2.
|
|
109
|
-
"@snap-rail/protocol": "0.1.1",
|
|
106
|
+
"@snap-rail/plugin-kit": "0.2.2",
|
|
107
|
+
"@snap-rail/plugin-sdk": "0.2.3",
|
|
110
108
|
"@snap-rail/settings": "0.1.0",
|
|
111
109
|
"@snap-rail/station-rpc": "0.1.0",
|
|
112
|
-
"@snap-rail/store-sqlite": "0.1.1"
|
|
110
|
+
"@snap-rail/store-sqlite": "0.1.1",
|
|
111
|
+
"@snap-rail/protocol": "0.1.3",
|
|
112
|
+
"@snap-rail/connection": "0.1.0"
|
|
113
113
|
},
|
|
114
114
|
"snapRail": {
|
|
115
115
|
"kind": "framework",
|