@joycostudio/studio 0.0.0-stage → 0.0.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.
@@ -0,0 +1,138 @@
1
+ import { DebugSchema } from '@joycostudio/xyz/debug';
2
+
3
+ type Success<T> = {
4
+ ok: true;
5
+ data: T;
6
+ };
7
+ type Failure = {
8
+ ok: false;
9
+ /** snake_case wire code, e.g. `invalid_config`. */
10
+ error: string;
11
+ message?: string;
12
+ };
13
+ type Result<T> = Success<T> | Failure;
14
+ declare const success: <T>(data: T) => Success<T>;
15
+ declare const error: (code: string, message?: string) => Failure;
16
+
17
+ /**
18
+ * An SVG icon as data: `[tag, attributes]` children of a 24×24 stroke icon.
19
+ * Lucide's core package (`import { Palette } from 'lucide'`) exports exactly this shape.
20
+ */
21
+ type ToolIcon = readonly (readonly [tag: string, attrs: Record<string, string | number | undefined>])[];
22
+ /**
23
+ * A tool's contract. Isomorphic on purpose: the runtime loads it in Node to
24
+ * validate and persist, the app loads it in the browser to apply and debug.
25
+ *
26
+ * `TConfig` is the one type that is read, edited, applied, and persisted.
27
+ */
28
+ type ToolDefinition<TConfig extends object = object> = {
29
+ /** Unique, URL-safe identifier. */
30
+ id: string;
31
+ title?: string;
32
+ /** Shown next to the tool in /studio. Data, not a component, so contracts stay loadable in Node. */
33
+ icon?: ToolIcon;
34
+ /** Repo-relative JSON file the config persists to. Import that same file as `initial`. */
35
+ file: string;
36
+ /** Config used before the runtime answers (and in production). */
37
+ initial: TConfig;
38
+ /**
39
+ * What can be tweaked, as an Xyz/Tweakpane schema bound to `config`.
40
+ * Doubles as the runtime validator: every control's bounds are enforced on write.
41
+ */
42
+ controls: (config: TConfig) => DebugSchema;
43
+ /** App route that renders this tool's result, shown next to the controls in /studio. */
44
+ preview?: string;
45
+ };
46
+ type AnyTool = ToolDefinition<any>;
47
+ type ConfigOf<TTool> = TTool extends ToolDefinition<infer TConfig> ? TConfig : never;
48
+ declare function defineTool<TConfig extends object>(tool: ToolDefinition<TConfig>): ToolDefinition<TConfig>;
49
+ type StudioConfig = {
50
+ tools: readonly AnyTool[];
51
+ /** Runtime WebSocket and HTTP MCP port. Defaults to 5179. */
52
+ port?: number;
53
+ /** Browser origins allowed on the bridge and MCP endpoint. Defaults to localhost origins. */
54
+ origins?: readonly string[];
55
+ /** Running app URL (e.g. http://localhost:3000), used for MCP preview captures. */
56
+ appUrl?: string;
57
+ };
58
+ declare const DEFAULT_PORT = 5179;
59
+ declare function defineConfig(config: StudioConfig): StudioConfig;
60
+
61
+ /** A tool's live state as owned by the runtime. */
62
+ type ToolState<TConfig = unknown> = {
63
+ tool: string;
64
+ /** What is being edited. Bumps on every accepted update. */
65
+ draft: {
66
+ value: TConfig;
67
+ revision: number;
68
+ };
69
+ /** What is on disk. `hash` is null while the file does not exist yet. */
70
+ saved: {
71
+ value: TConfig;
72
+ hash: string | null;
73
+ };
74
+ dirty: boolean;
75
+ /** The file changed on disk while the draft had unsaved edits. */
76
+ conflict: boolean;
77
+ };
78
+ type ToolSummary = {
79
+ id: string;
80
+ title: string;
81
+ file: string;
82
+ icon?: ToolIcon;
83
+ preview?: string;
84
+ };
85
+ /** Every operation the runtime authorizes. The UI, the in-app debug pane, and MCP all go through these. */
86
+ type Operations = {
87
+ 'tools.list': {
88
+ params: {};
89
+ result: ToolSummary[];
90
+ };
91
+ 'tool.get': {
92
+ params: {
93
+ tool: string;
94
+ };
95
+ result: ToolState;
96
+ };
97
+ /** Agents supply `revision` to avoid replacing a newer draft; browser controls can stream edits without it. */
98
+ 'tool.update': {
99
+ params: {
100
+ tool: string;
101
+ value: unknown;
102
+ revision?: number;
103
+ };
104
+ result: ToolState;
105
+ };
106
+ /** `revision` is the draft revision being approved; a newer draft rejects the save. */
107
+ 'tool.save': {
108
+ params: {
109
+ tool: string;
110
+ revision: number;
111
+ force?: boolean;
112
+ };
113
+ result: ToolState;
114
+ };
115
+ 'tool.discard': {
116
+ params: {
117
+ tool: string;
118
+ revision?: number;
119
+ };
120
+ result: ToolState;
121
+ };
122
+ };
123
+ type Op = keyof Operations;
124
+ type Request<TOp extends Op = Op> = {
125
+ id: number;
126
+ op: TOp;
127
+ params: Operations[TOp]['params'];
128
+ };
129
+ type ServerMessage = {
130
+ type: 'response';
131
+ id: number;
132
+ result: Result<unknown>;
133
+ } | {
134
+ type: 'state';
135
+ state: ToolState;
136
+ };
137
+
138
+ export { type AnyTool as A, type ConfigOf as C, DEFAULT_PORT as D, type Failure as F, type Op as O, type Result as R, type ServerMessage as S, type ToolDefinition as T, type ToolState as a, type Operations as b, type Request as c, type StudioConfig as d, type Success as e, type ToolIcon as f, type ToolSummary as g, defineConfig as h, defineTool as i, error as j, success as s };
@@ -0,0 +1,130 @@
1
+ import * as react from 'react';
2
+ import { DebugSchema, DebugFolder } from '@joycostudio/xyz/debug';
3
+
4
+ type Success<T> = {
5
+ ok: true;
6
+ data: T;
7
+ };
8
+ type Failure = {
9
+ ok: false;
10
+ /** snake_case wire code, e.g. `invalid_config`. */
11
+ error: string;
12
+ message?: string;
13
+ };
14
+ type Result<T> = Success<T> | Failure;
15
+
16
+ /**
17
+ * An SVG icon as data: `[tag, attributes]` children of a 24×24 stroke icon.
18
+ * Lucide's core package (`import { Palette } from 'lucide'`) exports exactly this shape.
19
+ */
20
+ type ToolIcon = readonly (readonly [tag: string, attrs: Record<string, string | number | undefined>])[];
21
+ /**
22
+ * A tool's contract. Isomorphic on purpose: the runtime loads it in Node to
23
+ * validate and persist, the app loads it in the browser to apply and debug.
24
+ *
25
+ * `TConfig` is the one type that is read, edited, applied, and persisted.
26
+ */
27
+ type ToolDefinition<TConfig extends object = object> = {
28
+ /** Unique, URL-safe identifier. */
29
+ id: string;
30
+ title?: string;
31
+ /** Shown next to the tool in /studio. Data, not a component, so contracts stay loadable in Node. */
32
+ icon?: ToolIcon;
33
+ /** Repo-relative JSON file the config persists to. Import that same file as `initial`. */
34
+ file: string;
35
+ /** Config used before the runtime answers (and in production). */
36
+ initial: TConfig;
37
+ /**
38
+ * What can be tweaked, as an Xyz/Tweakpane schema bound to `config`.
39
+ * Doubles as the runtime validator: every control's bounds are enforced on write.
40
+ */
41
+ controls: (config: TConfig) => DebugSchema;
42
+ /** App route that renders this tool's result, shown next to the controls in /studio. */
43
+ preview?: string;
44
+ };
45
+
46
+ /** A tool's live state as owned by the runtime. */
47
+ type ToolState<TConfig = unknown> = {
48
+ tool: string;
49
+ /** What is being edited. Bumps on every accepted update. */
50
+ draft: {
51
+ value: TConfig;
52
+ revision: number;
53
+ };
54
+ /** What is on disk. `hash` is null while the file does not exist yet. */
55
+ saved: {
56
+ value: TConfig;
57
+ hash: string | null;
58
+ };
59
+ dirty: boolean;
60
+ /** The file changed on disk while the draft had unsaved edits. */
61
+ conflict: boolean;
62
+ };
63
+
64
+ type Connection = {
65
+ phase: 'local';
66
+ } | {
67
+ phase: 'connecting';
68
+ } | {
69
+ phase: 'connected';
70
+ } | {
71
+ phase: 'offline';
72
+ };
73
+
74
+ type ToolHandle<TConfig extends object = any> = {
75
+ readonly definition: ToolDefinition<TConfig>;
76
+ /** The live config. Read it to apply; writes (from any pane, or your code) sync to the runtime. */
77
+ readonly config: TConfig;
78
+ /** Runtime-owned state; null until the runtime answers (always null without one). */
79
+ readonly state: ToolState<TConfig> | null;
80
+ /** Bumps on every config change. Handy as a React snapshot. */
81
+ readonly version: number;
82
+ /** Called after every config change, local or remote. */
83
+ subscribe(listener: (config: TConfig) => void): () => void;
84
+ /** The tool's Xyz schema bound to the live config. */
85
+ controls(): DebugSchema;
86
+ /** Xyz `Debuggable`: `debugManager.register(handle)` mounts this tool in the app's floating pane. */
87
+ debug(): DebugFolder;
88
+ /** True while `compare(true)` shows the saved config in place of the draft. */
89
+ readonly comparing: boolean;
90
+ /**
91
+ * Temporarily show the saved config (true) or go back to the draft (false). Local only: nothing
92
+ * reaches the runtime, and edits made while comparing are dropped when it ends.
93
+ */
94
+ compare(showSaved: boolean): void;
95
+ save(options?: {
96
+ force?: boolean;
97
+ }): Promise<Result<ToolState<TConfig>>>;
98
+ discard(): Promise<Result<ToolState<TConfig>>>;
99
+ };
100
+ type Studio$1 = {
101
+ readonly tools: readonly ToolHandle[];
102
+ tool<TConfig extends object>(definition: ToolDefinition<TConfig>): ToolHandle<TConfig>;
103
+ readonly connection: Connection;
104
+ /** Bumps on connection or any tool state change. */
105
+ readonly version: number;
106
+ subscribe(listener: () => void): () => void;
107
+ close(): void;
108
+ };
109
+
110
+ /** The /studio route: every registered tool, its preview, and its Tweakpane inspector. */
111
+ declare function Studio({ studio }: {
112
+ studio: Studio$1;
113
+ }): react.JSX.Element | null;
114
+
115
+ type StudioCommandMenuProps = {
116
+ studio: Studio$1;
117
+ /** Path to the host's Studio route. Defaults to /studio. */
118
+ studioPath?: string;
119
+ /** Optional host router navigation (for example, Next.js router.push). */
120
+ navigate?: (href: string) => void;
121
+ };
122
+ /** Mount once in the host layout, including /studio, to jump to any registered tool with ⌘/Ctrl+K. */
123
+ declare function StudioCommandMenu({ studio, studioPath, navigate }: StudioCommandMenuProps): react.JSX.Element;
124
+
125
+ /** Re-render on connection and tool state changes. */
126
+ declare function useStudio(studio: Studio$1): Studio$1;
127
+ /** The tool's live config; re-renders on every change, local or remote. */
128
+ declare function useToolConfig<TConfig extends object>(handle: ToolHandle<TConfig>): TConfig;
129
+
130
+ export { Studio, StudioCommandMenu, type StudioCommandMenuProps, useStudio, useToolConfig };