@gjsify/storybook-core 0.11.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/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/controller.js +1 -0
- package/lib/esm/controls.js +1 -0
- package/lib/esm/devtools.js +1 -0
- package/lib/esm/discover.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/registry.js +1 -0
- package/lib/esm/story-view-base.js +1 -0
- package/lib/types/controller.d.ts +95 -0
- package/lib/types/controller.spec.d.ts +2 -0
- package/lib/types/controls.d.ts +60 -0
- package/lib/types/controls.spec.d.ts +2 -0
- package/lib/types/devtools.d.ts +30 -0
- package/lib/types/discover.d.ts +12 -0
- package/lib/types/discover.spec.d.ts +2 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/registry.d.ts +38 -0
- package/lib/types/registry.spec.d.ts +2 -0
- package/lib/types/story-view-base.d.ts +74 -0
- package/lib/types/story-view-base.spec.d.ts +2 -0
- package/package.json +66 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{StoryRegistry as e}from"./registry.js";var StorybookController=class{constructor(t,n){this._registry=new e,this._activeStory=null,this._unsubArgs=null,this._view=t,this._buildControls=n}mount(e,t=!0){this._registry.registerStories(e);let n=this._registry.createStoryInstances();if(this._view.renderSidebar(this._groupByCategory(n),e=>this.select(e)),t){let e=this._firstStory();e&&this.select(e)}}select(e){this._view.setPreviewTitle(`${e.meta.title} — ${e.story}`),this._view.showPreview(e),this._view.markSelected(e.meta.title),this._wireControls(e),this._activeStory=e}_wireControls(e){this._unsubArgs&&=(this._unsubArgs(),null);let t=this._buildControls(e),n=t.map(e=>e.refresh);this._view.renderControls(t),this._unsubArgs=e.onArgsChanged(e=>{for(let t of n)t(e)})}_groupByCategory(e){let t=new Map;for(let n of e)for(let e of n.instances??[]){let n=e.meta.title.split(`/`),r=n[0],i=n.length>1?n[1]:e.meta.title;t.has(r)||t.set(r,[]),t.get(r).push({instance:e,name:i||`Unnamed`})}return[...t].map(([e,t])=>({category:e,stories:t}))}get activeStory(){return this._activeStory}listStories(){return this._registry.getStories().flatMap(e=>(e.instances??[]).map(e=>({title:e.meta.title,story:e.story,category:e.meta.title.split(`/`)[0],controls:e.meta.controls??[]})))}openStoryByTitle(e){let t=this._findByTitle(e);return t?(this.select(t),!0):!1}getCurrentStory(){let e=this._activeStory;return e?{title:e.meta.title,story:e.story,args:e.args}:null}setActiveArg(e,t){let n=this._activeStory;return n?(n.setArg(e,t),!0):!1}_findByTitle(e){for(let t of this._registry.getStories())for(let n of t.instances??[])if(n.meta.title===e)return n;return null}_firstStory(){for(let e of this._registry.getStories()){let t=(e.instances??[])[0];if(t)return t}return null}};export{StorybookController};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{ControlType as e}from"@gjsify/stories";function controlLabel(e){return e.label||e.name}function bindControl(t,n,r){let i=n.name,a=n.description;switch(n.type){case e.TEXT:{let e=r.text(controlLabel(n),a),o=t.args[i];return e.set(typeof o==`string`?o:``),e.onChange(e=>t.setArg(i,e)),{view:e.node,refresh:t=>{let n=typeof t[i]==`string`?t[i]:``;e.get()!==n&&e.set(n)}}}case e.COLOR:{let e=r.color(controlLabel(n),a),o=t.args[i];return e.set(typeof o==`string`?o:`#000000`),e.onChange(e=>t.setArg(i,e)),{view:e.node,refresh:t=>{let n=typeof t[i]==`string`?t[i]:`#000000`;e.get()!==n&&e.set(n)}}}case e.BOOLEAN:{let e=r.boolean(controlLabel(n),a);return e.set(!!t.args[i]),e.onChange(e=>t.setArg(i,e)),{view:e.node,refresh:t=>{let n=!!t[i];e.get()!==n&&e.set(n)}}}case e.NUMBER:{let e=r.number(n),a=t.args[i];return e.set(typeof a==`number`?a:n.min??0),e.onChange(e=>t.setArg(i,e)),{view:e.node,refresh:t=>{let n=typeof t[i]==`number`?t[i]:0;e.get()!==n&&e.set(n)}}}case e.RANGE:{let e=r.range(n),a=t.args[i],o=typeof a==`number`?a:n.min??0;return e.set(o),e.onChange(e=>t.setArg(i,e)),{view:e.node,refresh:t=>{let n=typeof t[i]==`number`?t[i]:o;e.get()!==n&&e.set(n)}}}case e.SELECT:{let e=n.options;if(!e?.length)return console.warn(`SELECT control "${i}" has no options`),null;let a=r.select(n),o=t.args[i],s=Math.max(0,e.findIndex(e=>e.value===o));return a.set(s),a.onChange(n=>{n>=0&&n<e.length&&t.setArg(i,e[n].value)}),{view:a.node,refresh:t=>{let n=t[i],r=e.findIndex(e=>e.value===n);r>=0&&a.get()!==r&&a.set(r)}}}default:return console.warn(`Unsupported control type: ${n.type}`),null}}export{bindControl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function buildStorybookDevtoolsExtension(e){let currentStory=()=>e.getCurrentStory();return{methods:[{name:`ListStories`,kind:`read-only`,handler:(()=>JSON.stringify(e.listStories(),null,2))},{name:`GetCurrentStory`,kind:`read-only`,handler:(()=>JSON.stringify(currentStory(),null,2))},{name:`OpenStory`,kind:`mutating`,handler:(t=>e.openStoryByTitle(t))},{name:`SetStoryArg`,kind:`mutating`,handler:((t,n)=>e.setActiveArg(t,n?JSON.parse(n):null))},{name:`GetStoryArgs`,kind:`read-only`,handler:(()=>JSON.stringify(currentStory()?.args??{},null,2))}],contributeStatus:()=>{let t=currentStory(),n=e.listStories();return{storybook:{currentStory:t?{title:t.title,story:t.story}:null,storyCount:n.length}}}}}export{buildStorybookDevtoolsExtension};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{isStoryModule as e}from"@gjsify/stories";function collectStoryModules(t){let n=[];for(let r of t)if(!(!r||typeof r!=`object`))for(let t of Object.values(r))e(t)&&n.push(t);return n}export{collectStoryModules};
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{StoryRegistry as e}from"./registry.js";import{StorybookController as t}from"./controller.js";import{bindControl as n}from"./controls.js";import{buildStorybookDevtoolsExtension as r}from"./devtools.js";import{collectStoryModules as i}from"./discover.js";import{StoryViewBase as a}from"./story-view-base.js";export*from"@gjsify/stories";export{e as StoryRegistry,a as StoryViewBase,t as StorybookController,n as bindControl,r as buildStorybookDevtoolsExtension,i as collectStoryModules};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";var StoryRegistry=class{constructor(){this.modules=[]}registerStory(e){if(!e||!Array.isArray(e.stories)){console.warn(`Invalid story module provided to registerStory`);return}this.modules.push(e)}registerStories(e){if(!Array.isArray(e)){console.warn(`Invalid story modules array provided to registerStories`);return}let t=e.filter(e=>e&&Array.isArray(e.stories));this.modules.push(...t)}getStories(){return[...this.modules]}createStoryInstances(){for(let e of this.modules)this.instantiateStoriesForModule(e);return[...this.modules]}instantiateStoriesForModule(e){e.instances||=[];let t=e.decorators??[];for(let n of e.stories)try{let r=new n,build=()=>{typeof r.initialize==`function`&&r.initialize()};t.reduceRight((e,t)=>()=>t(e,r),build)(),e.instances.push(r)}catch(e){console.error(`Failed to instantiate story: ${n.name||`Unknown`}`,e)}}};export{StoryRegistry};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{argsFromControls as e}from"@gjsify/stories";var StoryViewBase=class{constructor(){this._listeners=new Set}initBase(t,n,r){this._meta=t,this._story=n,this._args=e(t.controls),this._chrome=this.createChrome(t,r),this.refreshChromeText()}get meta(){return this._meta}get story(){return this._story}get args(){return this._args}get root(){return this._chrome.root}set args(e){this._args!==e&&(this._args=e,this.updateArgs(e),this.emitArgs(e))}emitArgs(e){for(let t of this._listeners)t(e)}onArgsChanged(e){return this._listeners.add(e),()=>{this._listeners.delete(e)}}setArg(e,t){this.args={...this._args,[e]:t}}initialize(){}updateArgs(e){}teardown(){}addContent(e){this._chrome.hasStage&&this._chrome.setStageContent(e)}refreshChromeText(){let e=this._story?`${this._meta.title} — ${this._story}`:this._meta.title;this._chrome.setChromeText(e,this._meta.description??``)}};export{StoryViewBase};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { StoryArgs, StoryArgValue, StoryControl } from '@gjsify/stories';
|
|
2
|
+
import type { ControlRow } from './controls.js';
|
|
3
|
+
import { type StoryModuleLike } from './registry.js';
|
|
4
|
+
/** The shape every renderer's story instance exposes to the controller. */
|
|
5
|
+
export interface StoryInstanceLike {
|
|
6
|
+
readonly meta: {
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
controls?: StoryControl[];
|
|
10
|
+
};
|
|
11
|
+
readonly story: string;
|
|
12
|
+
readonly args: StoryArgs;
|
|
13
|
+
initialize(): void;
|
|
14
|
+
setArg(name: string, value: StoryArgValue): void;
|
|
15
|
+
onArgsChanged(listener: (args: StoryArgs) => void): () => void;
|
|
16
|
+
}
|
|
17
|
+
/** A category group for the sidebar — the category name + its story instances. */
|
|
18
|
+
export interface CategoryGroup<TInstance> {
|
|
19
|
+
/** The `Category` prefix of the stories' titles. */
|
|
20
|
+
category: string;
|
|
21
|
+
/** `{ instance, name }` per story — `name` is the post-`/` part of the title. */
|
|
22
|
+
stories: Array<{
|
|
23
|
+
instance: TInstance;
|
|
24
|
+
name: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
/** A flat description of a story, returned by {@link StorybookController.listStories}. */
|
|
28
|
+
export interface StorySummary {
|
|
29
|
+
title: string;
|
|
30
|
+
story: string;
|
|
31
|
+
category: string;
|
|
32
|
+
controls: unknown[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The render seams the controller drives — each is a renderer-specific view
|
|
36
|
+
* operation. The breakpoint/fold is NOT here (adapter-owned).
|
|
37
|
+
*
|
|
38
|
+
* @typeParam TInstance the renderer's story-instance type.
|
|
39
|
+
*/
|
|
40
|
+
export interface StorybookView<TInstance> {
|
|
41
|
+
/** Build the category-grouped story list; `onSelect(instance)` selects a story. */
|
|
42
|
+
renderSidebar(groups: Array<CategoryGroup<TInstance>>, onSelect: (instance: TInstance) => void): void;
|
|
43
|
+
/** Mark the sidebar row for `title` as selected (others deselected). */
|
|
44
|
+
markSelected(title: string): void;
|
|
45
|
+
/** Set the preview header title. */
|
|
46
|
+
setPreviewTitle(title: string): void;
|
|
47
|
+
/** Show `instance` in the preview area. */
|
|
48
|
+
showPreview(instance: TInstance): void;
|
|
49
|
+
/** Replace the controls panel with `rows` (already built + wired). */
|
|
50
|
+
renderControls(rows: Array<ControlRow<unknown>>): void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Drives a storybook: registers + instantiates modules, groups them, shows a
|
|
54
|
+
* story and wires its controls, and exposes the MCP/devtools control surface.
|
|
55
|
+
*
|
|
56
|
+
* @typeParam TInstance the renderer's story-instance type.
|
|
57
|
+
*/
|
|
58
|
+
export declare class StorybookController<TInstance extends StoryInstanceLike> {
|
|
59
|
+
private _registry;
|
|
60
|
+
private _view;
|
|
61
|
+
private _buildControls;
|
|
62
|
+
private _activeStory;
|
|
63
|
+
private _unsubArgs;
|
|
64
|
+
/**
|
|
65
|
+
* @param view the render seams.
|
|
66
|
+
* @param buildControls builds + wires the control rows for a story (typically
|
|
67
|
+
* `instance.meta.controls.map(c => bindControl(...))`).
|
|
68
|
+
*/
|
|
69
|
+
constructor(view: StorybookView<TInstance>, buildControls: (instance: TInstance) => Array<ControlRow<unknown>>);
|
|
70
|
+
/**
|
|
71
|
+
* Register + instantiate `modules`, render the sidebar, and (when
|
|
72
|
+
* `openFirst`) select the first story.
|
|
73
|
+
*/
|
|
74
|
+
mount(modules: StoryModuleLike<TInstance, new () => TInstance>[], openFirst?: boolean): void;
|
|
75
|
+
/** Select + show a story instance, wiring its controls. */
|
|
76
|
+
select(instance: TInstance): void;
|
|
77
|
+
private _wireControls;
|
|
78
|
+
private _groupByCategory;
|
|
79
|
+
/** The currently-displayed story, or null. */
|
|
80
|
+
get activeStory(): TInstance | null;
|
|
81
|
+
/** Every story flattened to a summary. */
|
|
82
|
+
listStories(): StorySummary[];
|
|
83
|
+
/** Select + show a story by its full `Category/Name` title. */
|
|
84
|
+
openStoryByTitle(title: string): boolean;
|
|
85
|
+
/** The active story as `{ title, story, args }`, or null. */
|
|
86
|
+
getCurrentStory(): {
|
|
87
|
+
title: string;
|
|
88
|
+
story: string;
|
|
89
|
+
args: StoryArgs;
|
|
90
|
+
} | null;
|
|
91
|
+
/** Set one arg on the active story — drives the same path as the controls. */
|
|
92
|
+
setActiveArg(name: string, value: StoryArgValue): boolean;
|
|
93
|
+
private _findByTitle;
|
|
94
|
+
private _firstStory;
|
|
95
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type StoryArgs, type StoryArgValue, type StoryControl, type StoryNumberControl, type StorySelectControl } from '@gjsify/stories';
|
|
2
|
+
/** A built control: the row's view node plus a refresher that re-syncs it from args. */
|
|
3
|
+
export interface ControlRow<TNode> {
|
|
4
|
+
/** The node appended into the controls panel. */
|
|
5
|
+
view: TNode;
|
|
6
|
+
/** Re-sync the widget from the story's current args (called on every args change). */
|
|
7
|
+
refresh: (args: StoryArgs) => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A leaf control widget the renderer builds. `bindControl` reads/writes its
|
|
11
|
+
* decoded value and subscribes to its change event — it knows nothing about the
|
|
12
|
+
* story args.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam TNode the renderer's view-node type.
|
|
15
|
+
* @typeParam V the widget's native value type (`string`, `boolean`,
|
|
16
|
+
* `number`; SELECT is index-valued).
|
|
17
|
+
*/
|
|
18
|
+
export interface ControlWidget<TNode, V> {
|
|
19
|
+
/** The widget's view node. */
|
|
20
|
+
node: TNode;
|
|
21
|
+
/** Read the widget's current value. */
|
|
22
|
+
get(): V;
|
|
23
|
+
/** Write the widget's value (without firing its change callback when avoidable). */
|
|
24
|
+
set(value: V): void;
|
|
25
|
+
/** Subscribe to user-driven value changes. */
|
|
26
|
+
onChange(cb: (value: V) => void): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Builds the leaf widget for each {@link ControlType}. The single
|
|
30
|
+
* renderer-specific seam: GTK builds Adwaita rows, the browser builds
|
|
31
|
+
* `adw-*` custom elements, NativeScript builds native Adwaita views.
|
|
32
|
+
*
|
|
33
|
+
* `select` is index-valued (the widget exposes the selected option index, not
|
|
34
|
+
* its value — `bindControl` maps index↔value). `number`/`range` receive the
|
|
35
|
+
* full {@link StoryNumberControl} so the widget can set min/max/step.
|
|
36
|
+
*/
|
|
37
|
+
export interface ControlWidgetFactory<TNode> {
|
|
38
|
+
text(label: string, desc: string | undefined): ControlWidget<TNode, string>;
|
|
39
|
+
boolean(label: string, desc: string | undefined): ControlWidget<TNode, boolean>;
|
|
40
|
+
number(control: StoryNumberControl): ControlWidget<TNode, number>;
|
|
41
|
+
range(control: StoryNumberControl): ControlWidget<TNode, number>;
|
|
42
|
+
/** Index-valued: the widget's value is the selected option's index. */
|
|
43
|
+
select(control: StorySelectControl): ControlWidget<TNode, number>;
|
|
44
|
+
color(label: string, desc: string | undefined): ControlWidget<TNode, string>;
|
|
45
|
+
}
|
|
46
|
+
/** The story surface `bindControl` drives — the args bag + the single-arg writer. */
|
|
47
|
+
export interface BindableStory {
|
|
48
|
+
readonly args: StoryArgs;
|
|
49
|
+
setArg(name: string, value: StoryArgValue): void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build + wire a single control row, or `null` when it cannot be rendered (an
|
|
53
|
+
* unsupported type, or a SELECT with no options).
|
|
54
|
+
*
|
|
55
|
+
* The widget's change callback writes the decoded value back through
|
|
56
|
+
* `story.setArg`; the returned `refresh` re-reads the arg and pushes it into the
|
|
57
|
+
* widget only when it differs (mirrors every renderer's `if (get() !== next)`
|
|
58
|
+
* guard, so a programmatic args change doesn't re-fire the widget's own event).
|
|
59
|
+
*/
|
|
60
|
+
export declare function bindControl<TNode>(story: BindableStory, control: StoryControl, factory: ControlWidgetFactory<TNode>): ControlRow<TNode> | null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { StorybookController, StoryInstanceLike } from './controller.js';
|
|
2
|
+
/** Pause classification for a devtools method (matches @gjsify/devtools-protocol's MethodKind). */
|
|
3
|
+
export type StorybookMethodKind = 'read-only' | 'mutating';
|
|
4
|
+
/** One renderer-neutral storybook devtools method. */
|
|
5
|
+
export interface StorybookDevtoolsMethod {
|
|
6
|
+
/** Method name (the same names the GTK extension used). */
|
|
7
|
+
name: string;
|
|
8
|
+
/** Whether the method mutates state (the pause guard rejects mutating methods when paused). */
|
|
9
|
+
kind: StorybookMethodKind;
|
|
10
|
+
/** Implementation — positional args, JSON-string-or-boolean return (the DBus shape). */
|
|
11
|
+
handler: (...args: never[]) => unknown;
|
|
12
|
+
}
|
|
13
|
+
/** The renderer-neutral storybook devtools surface. */
|
|
14
|
+
export interface StorybookDevtoolsExtension {
|
|
15
|
+
/** The five storybook methods, keyed by name + as an ordered list. */
|
|
16
|
+
methods: StorybookDevtoolsMethod[];
|
|
17
|
+
/** Extra keys for `GetStatus()` — `{ storybook: { currentStory, storyCount } }`. */
|
|
18
|
+
contributeStatus: () => Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the renderer-neutral storybook devtools surface over a controller. The
|
|
22
|
+
* five methods preserve the names + JSON shapes the GTK extension defined:
|
|
23
|
+
*
|
|
24
|
+
* - `ListStories` → JSON array of {@link StorySummary} (read-only)
|
|
25
|
+
* - `GetCurrentStory` → JSON `{ title, story, args } | null` (read-only)
|
|
26
|
+
* - `OpenStory(title)`→ boolean opened (mutating)
|
|
27
|
+
* - `SetStoryArg(name, valueJson)` → boolean applied (mutating)
|
|
28
|
+
* - `GetStoryArgs` → JSON of the active story's args (read-only)
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildStorybookDevtoolsExtension<TInstance extends StoryInstanceLike>(controller: StorybookController<TInstance>): StorybookDevtoolsExtension;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StoryModuleLike } from './registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Flatten a list of module namespaces (the `import * as m from './x.story.js'`
|
|
4
|
+
* objects the `gjsify storybook` command generates) into the story modules they
|
|
5
|
+
* export — regardless of the export name. Any exported value shaped like a story
|
|
6
|
+
* module (an object with a `stories` array) is collected.
|
|
7
|
+
*
|
|
8
|
+
* @typeParam TInstance the renderer's story-instance type.
|
|
9
|
+
*/
|
|
10
|
+
export declare function collectStoryModules<TInstance extends {
|
|
11
|
+
initialize(): void;
|
|
12
|
+
}>(namespaces: Array<Record<string, unknown>>): StoryModuleLike<TInstance, new () => TInstance>[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A story module — a group of related story constructors plus optional
|
|
3
|
+
* module-level decorators wrapping each story's `initialize()`.
|
|
4
|
+
*
|
|
5
|
+
* @typeParam TInstance the renderer's story-instance type (it must expose
|
|
6
|
+
* `initialize()`).
|
|
7
|
+
* @typeParam TCtor the zero-arg constructor producing `TInstance`.
|
|
8
|
+
*/
|
|
9
|
+
export interface StoryModuleLike<TInstance extends {
|
|
10
|
+
initialize(): void;
|
|
11
|
+
}, TCtor extends new () => TInstance> {
|
|
12
|
+
/** The story constructors in this module. */
|
|
13
|
+
stories: TCtor[];
|
|
14
|
+
/** Populated by {@link StoryRegistry.createStoryInstances}. */
|
|
15
|
+
instances?: TInstance[];
|
|
16
|
+
/** Decorators applied around every story's `initialize()` in this module. */
|
|
17
|
+
decorators?: Array<(build: () => void, instance: TInstance) => void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Collects story modules and instantiates their stories. One per storybook run
|
|
21
|
+
* (no shared singleton).
|
|
22
|
+
*
|
|
23
|
+
* @typeParam TInstance the renderer's story-instance type.
|
|
24
|
+
*/
|
|
25
|
+
export declare class StoryRegistry<TInstance extends {
|
|
26
|
+
initialize(): void;
|
|
27
|
+
}> {
|
|
28
|
+
private modules;
|
|
29
|
+
/** Register a single story module. */
|
|
30
|
+
registerStory(storyModule: StoryModuleLike<TInstance, new () => TInstance>): void;
|
|
31
|
+
/** Register multiple story modules (invalid ones are filtered out). */
|
|
32
|
+
registerStories(storyModules: StoryModuleLike<TInstance, new () => TInstance>[]): void;
|
|
33
|
+
/** All registered story modules (a shallow copy). */
|
|
34
|
+
getStories(): StoryModuleLike<TInstance, new () => TInstance>[];
|
|
35
|
+
/** Instantiate every registered story and run its `initialize()`. */
|
|
36
|
+
createStoryInstances(): StoryModuleLike<TInstance, new () => TInstance>[];
|
|
37
|
+
private instantiateStoriesForModule;
|
|
38
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type StoryArgs, type StoryArgValue, type StoryMeta } from '@gjsify/stories';
|
|
2
|
+
/**
|
|
3
|
+
* The renderer-built chrome around a story's preview "stage". The default chrome
|
|
4
|
+
* is a clamp → boxed group → header(title, description) → `.story-stage`; a
|
|
5
|
+
* subclass passing a custom root opts out (then {@link hasStage} is false and
|
|
6
|
+
* {@link addContent} is a no-op).
|
|
7
|
+
*
|
|
8
|
+
* @typeParam TNode the renderer's view-node type (`Gtk.Widget`, `HTMLElement`,
|
|
9
|
+
* NativeScript `View`).
|
|
10
|
+
*/
|
|
11
|
+
export interface StoryChrome<TNode> {
|
|
12
|
+
/** The root node inserted into the storybook's preview area. */
|
|
13
|
+
readonly root: TNode;
|
|
14
|
+
/** Replace the preview stage's content (no-op for a custom root). */
|
|
15
|
+
setStageContent(child: TNode): void;
|
|
16
|
+
/** Whether a default stage exists (false for a custom root). */
|
|
17
|
+
readonly hasStage: boolean;
|
|
18
|
+
/** Update the header's title + description text. */
|
|
19
|
+
setChromeText(title: string, description: string): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Base class for a single story's view, parameterised by the renderer's node
|
|
23
|
+
* type. A renderer's story base (`StoryWidget` / `StoryElement` / `StoryView`)
|
|
24
|
+
* extends this, implements {@link createChrome}, and calls {@link initBase} from
|
|
25
|
+
* its own constructor (a base constructor cannot call an abstract method before
|
|
26
|
+
* the subclass's field initialisers have run, so init is explicit).
|
|
27
|
+
*
|
|
28
|
+
* @typeParam TNode the renderer's view-node type.
|
|
29
|
+
*/
|
|
30
|
+
export declare abstract class StoryViewBase<TNode> {
|
|
31
|
+
protected _meta: StoryMeta;
|
|
32
|
+
protected _story: string;
|
|
33
|
+
protected _args: StoryArgs;
|
|
34
|
+
private _listeners;
|
|
35
|
+
private _chrome;
|
|
36
|
+
/**
|
|
37
|
+
* Build the renderer's chrome. Called once from {@link initBase}. With
|
|
38
|
+
* `customRoot` set, return a chrome whose `root` is that node and whose
|
|
39
|
+
* `hasStage` is false (the default-chrome path otherwise).
|
|
40
|
+
*/
|
|
41
|
+
protected abstract createChrome(meta: StoryMeta, customRoot?: TNode): StoryChrome<TNode>;
|
|
42
|
+
/**
|
|
43
|
+
* Initialise the base state + chrome. Call from the subclass constructor
|
|
44
|
+
* (NOT the base constructor — an abstract `createChrome` must not run before
|
|
45
|
+
* the subclass's own fields are initialised).
|
|
46
|
+
*/
|
|
47
|
+
protected initBase(meta: StoryMeta, story: string, customRoot?: TNode): void;
|
|
48
|
+
get meta(): StoryMeta;
|
|
49
|
+
get story(): string;
|
|
50
|
+
get args(): StoryArgs;
|
|
51
|
+
/** The root node the storybook inserts into its preview area. */
|
|
52
|
+
get root(): TNode;
|
|
53
|
+
set args(value: StoryArgs);
|
|
54
|
+
/**
|
|
55
|
+
* Notify args listeners. Overridable so a renderer can ALSO fire its own
|
|
56
|
+
* change signal (e.g. the GTK adapter's GObject `notify('args')`) in the
|
|
57
|
+
* same path.
|
|
58
|
+
*/
|
|
59
|
+
protected emitArgs(value: StoryArgs): void;
|
|
60
|
+
/** Subscribe to args changes; returns an unsubscribe function. */
|
|
61
|
+
onArgsChanged(listener: (args: StoryArgs) => void): () => void;
|
|
62
|
+
/** Set a single arg — drives {@link updateArgs} and notifies listeners. */
|
|
63
|
+
setArg(name: string, value: StoryArgValue): void;
|
|
64
|
+
/** Override in subclasses — build the preview. Runs in the registry init path. */
|
|
65
|
+
initialize(): void;
|
|
66
|
+
/** Override in subclasses — react to a control change. */
|
|
67
|
+
updateArgs(_args: StoryArgs): void;
|
|
68
|
+
/** Override in subclasses to release resources on deselect. */
|
|
69
|
+
teardown(): void;
|
|
70
|
+
/** Replace the default preview slot's content. No-op with a custom root. */
|
|
71
|
+
addContent(child: TNode): void;
|
|
72
|
+
/** Recompute + push the `"<title> — <story>"` header text into the chrome. */
|
|
73
|
+
protected refreshChromeText(): void;
|
|
74
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gjsify/storybook-core",
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "Renderer-agnostic storybook logic (story base, registry, control binding, controller, discovery, devtools) shared by the GTK, browser and NativeScript storybook renderers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "lib/esm/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/esm/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"lib"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"clear": "rm -rf lib tmp tsconfig.tsbuildinfo test.gjs.mjs test.node.mjs dist || exit 0",
|
|
19
|
+
"check": "gjsify tsc --noEmit",
|
|
20
|
+
"build": "gjsify run build:gjsify && gjsify run build:types",
|
|
21
|
+
"build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
|
|
22
|
+
"build:types": "gjsify tsc",
|
|
23
|
+
"build:test": "gjsify run build:test:gjs && gjsify run build:test:node && gjsify run build:test:browser",
|
|
24
|
+
"build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
|
|
25
|
+
"build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
|
|
26
|
+
"build:test:browser": "gjsify build src/test.browser.mts --app browser --outfile dist/test.browser.mjs",
|
|
27
|
+
"test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:gjs",
|
|
28
|
+
"test:gjs": "gjsify run test.gjs.mjs",
|
|
29
|
+
"test:node": "node test.node.mjs"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"gjs",
|
|
33
|
+
"node",
|
|
34
|
+
"browser",
|
|
35
|
+
"nativescript",
|
|
36
|
+
"storybook",
|
|
37
|
+
"stories"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@gjsify/stories": "^0.11.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@gjsify/cli": "^0.11.0",
|
|
44
|
+
"@gjsify/unit": "^0.11.0",
|
|
45
|
+
"@types/node": "^25.9.2",
|
|
46
|
+
"typescript": "^6.0.3"
|
|
47
|
+
},
|
|
48
|
+
"gjsify": {
|
|
49
|
+
"runtimes": {
|
|
50
|
+
"gjs": "polyfill",
|
|
51
|
+
"node": "polyfill",
|
|
52
|
+
"browser": "polyfill",
|
|
53
|
+
"nativescript": "polyfill"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/gjsify/gjsify.git",
|
|
60
|
+
"directory": "packages/framework/storybook-core"
|
|
61
|
+
},
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/gjsify/gjsify/issues"
|
|
64
|
+
},
|
|
65
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/framework/storybook-core#readme"
|
|
66
|
+
}
|