@substrate-kit/cli 0.1.2 → 0.2.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/.claude-plugin/plugin.json +1 -1
- package/LICENSE +21 -0
- package/README.md +25 -8
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -41
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts +1 -0
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +51 -109
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/docs.d.ts +4 -2
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +33 -54
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +158 -122
- package/dist/commands/init.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +8 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/get-component.d.ts +1 -1
- package/dist/mcp/tools/get-component.d.ts.map +1 -1
- package/dist/mcp/tools/get-component.js +10 -6
- package/dist/mcp/tools/get-component.js.map +1 -1
- package/dist/mcp/tools/get-docs.d.ts +17 -0
- package/dist/mcp/tools/get-docs.d.ts.map +1 -0
- package/dist/mcp/tools/get-docs.js +23 -0
- package/dist/mcp/tools/get-docs.js.map +1 -0
- package/dist/mcp/tools/install.d.ts +2 -1
- package/dist/mcp/tools/install.d.ts.map +1 -1
- package/dist/mcp/tools/install.js +18 -27
- package/dist/mcp/tools/install.js.map +1 -1
- package/dist/mcp/tools/search.d.ts +1 -1
- package/dist/mcp/tools/search.d.ts.map +1 -1
- package/dist/mcp/tools/search.js +2 -4
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/{mcp/types.d.ts → registry.d.ts} +6 -1
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +48 -0
- package/dist/registry.js.map +1 -0
- package/dist/utils/agents-file.d.ts +4 -0
- package/dist/utils/agents-file.d.ts.map +1 -0
- package/dist/utils/agents-file.js +36 -0
- package/dist/utils/agents-file.js.map +1 -0
- package/dist/utils/docs.d.ts +7 -0
- package/dist/utils/docs.d.ts.map +1 -0
- package/dist/utils/docs.js +28 -0
- package/dist/utils/docs.js.map +1 -0
- package/dist/utils/shadcn.d.ts +6 -1
- package/dist/utils/shadcn.d.ts.map +1 -1
- package/dist/utils/shadcn.js +25 -12
- package/dist/utils/shadcn.js.map +1 -1
- package/dist/utils/version.d.ts +3 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +8 -0
- package/dist/utils/version.js.map +1 -0
- package/docs/components/action.mdx +30 -0
- package/docs/components/alignment-controls.mdx +30 -0
- package/docs/components/badge.mdx +22 -0
- package/docs/components/breadcrumbs.mdx +25 -0
- package/docs/components/collapsible-pane.mdx +35 -0
- package/docs/components/colour-picker.mdx +66 -0
- package/docs/components/command-palette.mdx +36 -0
- package/docs/components/context-menu.mdx +31 -0
- package/docs/components/controls-timeline.mdx +107 -0
- package/docs/components/controls.mdx +168 -0
- package/docs/components/design-canvas.mdx +51 -0
- package/docs/components/document-tabs.mdx +25 -0
- package/docs/components/easing-curve-editor.mdx +85 -0
- package/docs/components/empty-state.mdx +26 -0
- package/docs/components/gallery.mdx +60 -0
- package/docs/components/history-controls.mdx +47 -0
- package/docs/components/image-picker.mdx +50 -0
- package/docs/components/layer-list.mdx +36 -0
- package/docs/components/node.mdx +85 -0
- package/docs/components/number-input.mdx +44 -0
- package/docs/components/number-slider.mdx +32 -0
- package/docs/components/pane.mdx +39 -0
- package/docs/components/panel.mdx +28 -0
- package/docs/components/popover.mdx +32 -0
- package/docs/components/progress-bar.mdx +24 -0
- package/docs/components/resizer.mdx +24 -0
- package/docs/components/select.mdx +31 -0
- package/docs/components/shortcut-overlay.mdx +43 -0
- package/docs/components/slider.mdx +20 -0
- package/docs/components/spring-editor.mdx +85 -0
- package/docs/components/status-bar.mdx +42 -0
- package/docs/components/surface.mdx +47 -0
- package/docs/components/swatch-palette.mdx +36 -0
- package/docs/components/text-area.mdx +26 -0
- package/docs/components/timeline.mdx +96 -0
- package/docs/components/toast.mdx +42 -0
- package/docs/components/toggle-group.mdx +40 -0
- package/docs/components/toggle.mdx +27 -0
- package/docs/components/toolbar.mdx +24 -0
- package/docs/components/tree-list.mdx +46 -0
- package/docs/components/xy-pad.mdx +49 -0
- package/docs/hooks/use-clipboard.mdx +70 -0
- package/docs/hooks/use-drag-reorder.mdx +60 -0
- package/docs/hooks/use-selection.mdx +68 -0
- package/docs/hooks/use-undoable.mdx +83 -0
- package/docs/hooks/use-viewport.mdx +75 -0
- package/docs/utilities/bounding-box.mdx +76 -0
- package/docs/utilities/keyboard-shortcuts.mdx +64 -0
- package/docs/utilities/snap.mdx +73 -0
- package/package.json +6 -4
- package/registry.json +274 -6
- package/skills/3d-scene/SKILL.md +2 -1
- package/skills/animation/SKILL.md +2 -1
- package/skills/canvas-2d/SKILL.md +67 -5
- package/skills/composing-panels/SKILL.md +1 -4
- package/skills/data-visualisation/SKILL.md +2 -1
- package/skills/image-generation/SKILL.md +2 -1
- package/skills/install-components/SKILL.md +22 -16
- package/skills/scaffold-playground/SKILL.md +39 -19
- package/skills/substrate-canvas/SKILL.md +5 -0
- package/skills/substrate-controls/SKILL.md +57 -2
- package/skills/substrate-interaction/SKILL.md +19 -10
- package/skills/substrate-scaffold/SKILL.md +4 -4
- package/skills/theming/SKILL.md +8 -2
- package/skills/wire-interactions/SKILL.md +6 -3
- package/templates/agents-section.md +41 -0
- package/dist/commands/browse.d.ts +0 -2
- package/dist/commands/browse.d.ts.map +0 -1
- package/dist/commands/browse.js +0 -25
- package/dist/commands/browse.js.map +0 -1
- package/dist/commands/create.d.ts +0 -9
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -27
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/variables.d.ts +0 -4
- package/dist/commands/variables.d.ts.map +0 -1
- package/dist/commands/variables.js +0 -111
- package/dist/commands/variables.js.map +0 -1
- package/dist/mcp/registry.d.ts +0 -3
- package/dist/mcp/registry.d.ts.map +0 -1
- package/dist/mcp/registry.js +0 -43
- package/dist/mcp/registry.js.map +0 -1
- package/dist/mcp/tools/theme.d.ts +0 -10
- package/dist/mcp/tools/theme.d.ts.map +0 -1
- package/dist/mcp/tools/theme.js +0 -37
- package/dist/mcp/tools/theme.js.map +0 -1
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/mcp/types.js +0 -2
- package/dist/mcp/types.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE/D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAgC5C"}
|
package/dist/mcp/tools/search.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { matchesQuery } from "../../registry.js";
|
|
1
2
|
export async function handleSearch(registry, args) {
|
|
2
3
|
let items = registry.items;
|
|
3
4
|
if (args.category) {
|
|
@@ -6,10 +7,7 @@ export async function handleSearch(registry, args) {
|
|
|
6
7
|
}
|
|
7
8
|
if (args.query) {
|
|
8
9
|
const q = args.query.toLowerCase();
|
|
9
|
-
items = items.filter((i) => i
|
|
10
|
-
(i.title || "").toLowerCase().includes(q) ||
|
|
11
|
-
(i.description || "").toLowerCase().includes(q) ||
|
|
12
|
-
(i.categories || []).some((c) => c.toLowerCase().includes(q)));
|
|
10
|
+
items = items.filter((i) => matchesQuery(i, q));
|
|
13
11
|
}
|
|
14
12
|
const results = items.map((i) => ({
|
|
15
13
|
name: i.name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/mcp/tools/search.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/mcp/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiB,MAAM,mBAAmB,CAAA;AAE/D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAkB,EAClB,IAA2C;IAE3C,IAAI,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAA;IAE1B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAA;QACvC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACzB,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAC1D,CAAA;IACH,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAA;QAClC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACjD,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChC,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,OAAO,EAAE,yBAAyB,CAAC,CAAC,IAAI,EAAE;KAC3C,CAAC,CAAC,CAAA;IAEH,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;aACvC;SACF;KACF,CAAA;AACH,CAAC"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export declare const SITE_URL = "https://substrate.georgedrury.co.uk";
|
|
2
|
+
export declare const REGISTRY_BASE: string;
|
|
1
3
|
export interface RegistryItem {
|
|
2
4
|
name: string;
|
|
3
5
|
type: string;
|
|
@@ -28,4 +30,7 @@ export interface RegistryItem {
|
|
|
28
30
|
export interface Registry {
|
|
29
31
|
items: RegistryItem[];
|
|
30
32
|
}
|
|
31
|
-
|
|
33
|
+
export declare function loadRegistry(): Promise<Registry>;
|
|
34
|
+
export declare function overrideKeys(item: RegistryItem): string[];
|
|
35
|
+
export declare function matchesQuery(item: RegistryItem, query: string): boolean;
|
|
36
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,QAAQ,wCAAwC,CAAA;AAG7D,eAAO,MAAM,aAAa,QAAwD,CAAA;AAKlF,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC/B,IAAI,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAChD,CAAA;IACD,OAAO,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAC7B,CAAA;IACD,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC7D;AAED,MAAM,WAAW,QAAQ;IACxB,KAAK,EAAE,YAAY,EAAE,CAAA;CACrB;AAKD,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,CAkBtD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CAOzD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAQvE"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { PACKAGE_ROOT } from "./utils/version.js";
|
|
4
|
+
export const SITE_URL = "https://substrate.georgedrury.co.uk";
|
|
5
|
+
// SUBSTRATE_REGISTRY_URL points installs at a local registry for testing
|
|
6
|
+
export const REGISTRY_BASE = process.env.SUBSTRATE_REGISTRY_URL ?? `${SITE_URL}/r`;
|
|
7
|
+
const REGISTRY_URL = `${SITE_URL}/registry.json`;
|
|
8
|
+
const FETCH_TIMEOUT_MS = 5000;
|
|
9
|
+
// The live registry is the newest; the copy bundled with the package keeps
|
|
10
|
+
// the CLI working offline. A registry.json in the current folder is never
|
|
11
|
+
// read, since it could belong to an unrelated project.
|
|
12
|
+
export async function loadRegistry() {
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
15
|
+
if (res.ok)
|
|
16
|
+
return (await res.json());
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
/* fall through to the bundled copy */
|
|
20
|
+
}
|
|
21
|
+
const bundled = join(PACKAGE_ROOT, "registry.json");
|
|
22
|
+
if (existsSync(bundled)) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(readFileSync(bundled, "utf-8"));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
/* fall through */
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error("Could not load the Substrate registry");
|
|
31
|
+
}
|
|
32
|
+
export function overrideKeys(item) {
|
|
33
|
+
const keys = [];
|
|
34
|
+
for (const value of Object.values(item.cssVars?.theme ?? {})) {
|
|
35
|
+
const match = value.match(/^var\(--([^,)]+)/);
|
|
36
|
+
if (match)
|
|
37
|
+
keys.push(`--${match[1]}`);
|
|
38
|
+
}
|
|
39
|
+
return keys;
|
|
40
|
+
}
|
|
41
|
+
export function matchesQuery(item, query) {
|
|
42
|
+
const q = query.toLowerCase();
|
|
43
|
+
return (item.name.includes(q) ||
|
|
44
|
+
(item.title ?? "").toLowerCase().includes(q) ||
|
|
45
|
+
(item.description ?? "").toLowerCase().includes(q) ||
|
|
46
|
+
(item.categories ?? []).some((c) => c.toLowerCase().includes(q)));
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,MAAM,CAAC,MAAM,QAAQ,GAAG,qCAAqC,CAAA;AAE7D,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,GAAG,QAAQ,IAAI,CAAA;AAElF,MAAM,YAAY,GAAG,GAAG,QAAQ,gBAAgB,CAAA;AAChD,MAAM,gBAAgB,GAAG,IAAI,CAAA;AA2B7B,2EAA2E;AAC3E,0EAA0E;AAC1E,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,YAAY;IACjC,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAA;QACxF,IAAI,GAAG,CAAC,EAAE;YAAE,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAa,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACR,sCAAsC;IACvC,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAA;IACnD,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACJ,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAa,CAAA;QAC9D,CAAC;QAAC,MAAM,CAAC;YACR,kBAAkB;QACnB,CAAC;IACF,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;AACzD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAkB;IAC9C,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;QAC7C,IAAI,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACtC,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAkB,EAAE,KAAa;IAC7D,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAA;IAC7B,OAAO,CACN,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrB,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC5C,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClD,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAChE,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents-file.d.ts","sourceRoot":"","sources":["../../src/utils/agents-file.ts"],"names":[],"mappings":"AAQA,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,CAAA;AAIvE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAuBvE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { exists, writeFile } from "./fs.js";
|
|
4
|
+
import { PACKAGE_ROOT } from "./version.js";
|
|
5
|
+
const START = "<!-- substrate:start -->";
|
|
6
|
+
const END = "<!-- substrate:end -->";
|
|
7
|
+
export function agentsSection() {
|
|
8
|
+
const body = readFileSync(join(PACKAGE_ROOT, "templates", "agents-section.md"), "utf-8").trimEnd();
|
|
9
|
+
return `${START}\n${body}\n${END}\n`;
|
|
10
|
+
}
|
|
11
|
+
// Puts the Substrate guidance between markers so it can be refreshed later
|
|
12
|
+
// without touching anything the project wrote itself.
|
|
13
|
+
export function mergeSection(file, section) {
|
|
14
|
+
if (!exists(file)) {
|
|
15
|
+
writeFile(file, section);
|
|
16
|
+
return "created";
|
|
17
|
+
}
|
|
18
|
+
const current = readFileSync(file, "utf-8");
|
|
19
|
+
const start = current.indexOf(START);
|
|
20
|
+
const end = current.indexOf(END);
|
|
21
|
+
let next;
|
|
22
|
+
let result;
|
|
23
|
+
if (start !== -1 && end > start) {
|
|
24
|
+
next = current.slice(0, start) + section.trimEnd() + current.slice(end + END.length);
|
|
25
|
+
result = "updated";
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
next = `${current.trimEnd()}\n\n${section}`;
|
|
29
|
+
result = "added";
|
|
30
|
+
}
|
|
31
|
+
if (next === current)
|
|
32
|
+
return "unchanged";
|
|
33
|
+
writeFile(file, next);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=agents-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents-file.js","sourceRoot":"","sources":["../../src/utils/agents-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3C,MAAM,KAAK,GAAG,0BAA0B,CAAA;AACxC,MAAM,GAAG,GAAG,wBAAwB,CAAA;AAEpC,MAAM,UAAU,aAAa;IAC5B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAA;IAClG,OAAO,GAAG,KAAK,KAAK,IAAI,KAAK,GAAG,IAAI,CAAA;AACrC,CAAC;AAID,2EAA2E;AAC3E,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,OAAe;IACzD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACxB,OAAO,SAAS,CAAA;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACpC,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAEhC,IAAI,IAAY,CAAA;IAChB,IAAI,MAAmB,CAAA;IACvB,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;QACjC,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAA;QACpF,MAAM,GAAG,SAAS,CAAA;IACnB,CAAC;SAAM,CAAC;QACP,IAAI,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,OAAO,EAAE,CAAA;QAC3C,MAAM,GAAG,OAAO,CAAA;IACjB,CAAC;IAED,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,WAAW,CAAA;IACxC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACrB,OAAO,MAAM,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/utils/docs.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,QAAQ;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAa1D;AAGD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { SITE_URL } from "../registry.js";
|
|
4
|
+
import { PACKAGE_ROOT } from "./version.js";
|
|
5
|
+
// Docs pages are copied into the package at publish time (see
|
|
6
|
+
// scripts/bundle-cli-assets.mjs), one folder per section of the docs site.
|
|
7
|
+
const DOCS_DIR = join(PACKAGE_ROOT, "docs");
|
|
8
|
+
export function findDocsPage(name) {
|
|
9
|
+
if (!/^[a-z0-9-]+$/.test(name) || !existsSync(DOCS_DIR))
|
|
10
|
+
return null;
|
|
11
|
+
for (const section of readdirSync(DOCS_DIR, { withFileTypes: true })) {
|
|
12
|
+
if (!section.isDirectory())
|
|
13
|
+
continue;
|
|
14
|
+
const file = join(DOCS_DIR, section.name, `${name}.mdx`);
|
|
15
|
+
if (existsSync(file)) {
|
|
16
|
+
return {
|
|
17
|
+
url: `${SITE_URL}/docs/${section.name}/${name}`,
|
|
18
|
+
content: readFileSync(file, "utf-8"),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
// Hooks and utilities live under their own sections of the docs site
|
|
25
|
+
export function docsUrl(name) {
|
|
26
|
+
return findDocsPage(name)?.url ?? `${SITE_URL}/docs/components/${name}`;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=docs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/utils/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3C,8DAA8D;AAC9D,2EAA2E;AAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;AAO3C,MAAM,UAAU,YAAY,CAAC,IAAY;IACxC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAA;IACpE,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACtE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;YAAE,SAAQ;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,IAAI,MAAM,CAAC,CAAA;QACxD,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,OAAO;gBACN,GAAG,EAAE,GAAG,QAAQ,SAAS,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE;gBAC/C,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC;aACpC,CAAA;QACF,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,OAAO,CAAC,IAAY;IACnC,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,QAAQ,oBAAoB,IAAI,EAAE,CAAA;AACxE,CAAC"}
|
package/dist/utils/shadcn.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ export interface ShadcnAddOptions {
|
|
|
2
2
|
cwd?: string;
|
|
3
3
|
/** Capture output instead of printing it (used by the MCP server) */
|
|
4
4
|
silent?: boolean;
|
|
5
|
+
/** Pass --yes so shadcn skips its confirmation prompts */
|
|
6
|
+
yes?: boolean;
|
|
7
|
+
/** How many times to try before giving up (default 3) */
|
|
8
|
+
attempts?: number;
|
|
5
9
|
}
|
|
6
|
-
export
|
|
10
|
+
export type RetryHandler = (attempt: number, error: unknown) => void;
|
|
11
|
+
export declare function shadcnAdd(urls: string[], options?: ShadcnAddOptions, onRetry?: RetryHandler): void;
|
|
7
12
|
//# sourceMappingURL=shadcn.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadcn.d.ts","sourceRoot":"","sources":["../../src/utils/shadcn.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"shadcn.d.ts","sourceRoot":"","sources":["../../src/utils/shadcn.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AAGD,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;AAOpE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,gBAAqB,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAoBtG"}
|
package/dist/utils/shadcn.js
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// shadcn
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
// One shadcn call installs every item, so shared dependencies are resolved
|
|
3
|
+
// once. shadcn asks before overwriting a file that differs from the registry;
|
|
4
|
+
// with no terminal to answer it exits successfully having written nothing,
|
|
5
|
+
// so answer "no" for it: existing files are kept and the rest are installed.
|
|
6
|
+
// A dropped request fails the whole call, so it is retried.
|
|
7
|
+
export function shadcnAdd(urls, options = {}, onRetry) {
|
|
8
|
+
const { cwd, silent = false, yes = false, attempts = 3 } = options;
|
|
7
9
|
const interactive = !silent && process.stdin.isTTY === true;
|
|
8
10
|
const output = silent ? "pipe" : "inherit";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
12
|
+
for (let attempt = 1;; attempt++) {
|
|
13
|
+
try {
|
|
14
|
+
execFileSync(npx, ["shadcn@latest", "add", ...(yes ? ["--yes"] : []), ...urls], {
|
|
15
|
+
cwd,
|
|
16
|
+
encoding: "utf-8",
|
|
17
|
+
stdio: interactive ? "inherit" : ["pipe", output, output],
|
|
18
|
+
input: interactive ? undefined : "n\n".repeat(200),
|
|
19
|
+
});
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (attempt >= attempts)
|
|
24
|
+
throw error;
|
|
25
|
+
onRetry?.(attempt, error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
15
28
|
}
|
|
16
29
|
//# sourceMappingURL=shadcn.js.map
|
package/dist/utils/shadcn.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadcn.js","sourceRoot":"","sources":["../../src/utils/shadcn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"shadcn.js","sourceRoot":"","sources":["../../src/utils/shadcn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAejD,2EAA2E;AAC3E,8EAA8E;AAC9E,2EAA2E;AAC3E,6EAA6E;AAC7E,4DAA4D;AAC5D,MAAM,UAAU,SAAS,CAAC,IAAc,EAAE,UAA4B,EAAE,EAAE,OAAsB;IAC/F,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,EAAE,GAAG,GAAG,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,GAAG,OAAO,CAAA;IAClE,MAAM,WAAW,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAA;IAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAA;IAE5D,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QACnC,IAAI,CAAC;YACJ,YAAY,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE;gBAC/E,GAAG;gBACH,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;gBACzD,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;aAClD,CAAC,CAAA;YACF,OAAM;QACP,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,OAAO,IAAI,QAAQ;gBAAE,MAAM,KAAK,CAAA;YACpC,OAAO,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC1B,CAAC;IACF,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/utils/version.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,QAAc,CAAA;AAEvC,eAAO,MAAM,OAAO,EAAE,MAEb,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
// Both src/utils and dist/utils sit two levels below the package root
|
|
5
|
+
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
6
|
+
export const PACKAGE_ROOT = packageRoot;
|
|
7
|
+
export const VERSION = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf-8")).version;
|
|
8
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/utils/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,sEAAsE;AACtE,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;AAE7E,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAA;AAEvC,MAAM,CAAC,MAAM,OAAO,GACnB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CACnE,CAAC,OAAO,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Action
|
|
3
|
+
description: Form field row layout for property editors.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/action
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Default
|
|
15
|
+
|
|
16
|
+
<ComponentPreview name="action-demo" />
|
|
17
|
+
|
|
18
|
+
### With colour picker
|
|
19
|
+
|
|
20
|
+
`ActionControls` automatically sizes and strips borders from nested `ColourSwatch`, `Slider`, `NumberInput`, and other slot-aware components.
|
|
21
|
+
|
|
22
|
+
<ComponentPreview name="action-colour" />
|
|
23
|
+
|
|
24
|
+
## Components
|
|
25
|
+
|
|
26
|
+
| Component | Description |
|
|
27
|
+
| ---------------- | -------------------------------------- |
|
|
28
|
+
| `Action` | Container with flex row layout and gap |
|
|
29
|
+
| `ActionLabel` | Fixed-width label |
|
|
30
|
+
| `ActionControls` | Flex-1 control area |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Alignment controls
|
|
3
|
+
description: Align and distribute selected elements – left, centre, right, top, middle, bottom.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/alignment-controls
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Default
|
|
15
|
+
|
|
16
|
+
The first row shows all controls including distribute. The second row hides distribute with `showDistribute={false}`.
|
|
17
|
+
|
|
18
|
+
<ComponentPreview name="alignment-controls-demo" />
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
| ---------------- | ------------------------------- | ------- | ----------------------------------- |
|
|
24
|
+
| `onAlign` | `(action: AlignAction) => void` | — | Called with the alignment action |
|
|
25
|
+
| `showDistribute` | `boolean` | `true` | Show distribute horizontal/vertical |
|
|
26
|
+
| `disabled` | `boolean` | — | Disables all buttons |
|
|
27
|
+
|
|
28
|
+
### AlignAction values
|
|
29
|
+
|
|
30
|
+
`left` · `center-h` · `right` · `top` · `center-v` · `bottom` · `distribute-h` · `distribute-v`
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Badge
|
|
3
|
+
description: Small label for element types, node categories, and status indicators.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/badge
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Default
|
|
15
|
+
|
|
16
|
+
<ComponentPreview name="badge-demo" />
|
|
17
|
+
|
|
18
|
+
## Props
|
|
19
|
+
|
|
20
|
+
| Prop | Type | Default | Description |
|
|
21
|
+
| --------- | -------------------------------------------------------- | ----------- | ------------ |
|
|
22
|
+
| `variant` | `"default"` \| `"secondary"` \| `"outline"` \| `"muted"` | `"default"` | Visual style |
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Breadcrumbs
|
|
3
|
+
description: Navigation path for drilling into nested frames and groups.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/breadcrumbs
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Default
|
|
15
|
+
|
|
16
|
+
Click any ancestor to navigate back up. The active (last) item is bold and non-clickable.
|
|
17
|
+
|
|
18
|
+
<ComponentPreview name="breadcrumbs-demo" />
|
|
19
|
+
|
|
20
|
+
## Components
|
|
21
|
+
|
|
22
|
+
| Component | Description |
|
|
23
|
+
| ---------------- | ------------------------------------------------ |
|
|
24
|
+
| `Breadcrumbs` | `nav` wrapper with horizontal flex layout |
|
|
25
|
+
| `BreadcrumbItem` | Clickable path segment with auto separator arrow |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Collapsible pane
|
|
3
|
+
description: Pane section with expand/collapse toggle. Built on Radix Collapsible with animated height transitions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/collapsible-pane
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Default
|
|
15
|
+
|
|
16
|
+
The first section starts open, the second starts collapsed. Click the chevron to toggle.
|
|
17
|
+
|
|
18
|
+
<ComponentPreview name="collapsible-pane-demo" />
|
|
19
|
+
|
|
20
|
+
## Components
|
|
21
|
+
|
|
22
|
+
| Component | Description |
|
|
23
|
+
| ------------------------ | ------------------------------------------- |
|
|
24
|
+
| `CollapsiblePane` | Root wrapper with Radix Collapsible state |
|
|
25
|
+
| `CollapsiblePaneHeader` | Header row with expand/collapse chevron |
|
|
26
|
+
| `CollapsiblePaneLabel` | Section title text |
|
|
27
|
+
| `CollapsiblePaneContent` | Animated body that collapses to zero height |
|
|
28
|
+
|
|
29
|
+
## Props (CollapsiblePane)
|
|
30
|
+
|
|
31
|
+
| Prop | Type | Default | Description |
|
|
32
|
+
| -------------- | ------------------------- | ------- | --------------------------------- |
|
|
33
|
+
| `defaultOpen` | `boolean` | `true` | Initial open state (uncontrolled) |
|
|
34
|
+
| `open` | `boolean` | — | Controlled open state |
|
|
35
|
+
| `onOpenChange` | `(open: boolean) => void` | — | Called when open state changes |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Colour picker
|
|
3
|
+
description: Hex colour picker with swatch trigger, popover panel, and text input — built on react-colorful and Radix Popover.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/colour-picker
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Default
|
|
15
|
+
|
|
16
|
+
<ComponentPreview name="colour-picker-demo" />
|
|
17
|
+
|
|
18
|
+
### Inline
|
|
19
|
+
|
|
20
|
+
<ComponentPreview name="colour-picker-inline" />
|
|
21
|
+
|
|
22
|
+
### Custom trigger
|
|
23
|
+
|
|
24
|
+
<ComponentPreview name="colour-picker-custom-trigger" />
|
|
25
|
+
|
|
26
|
+
## Components
|
|
27
|
+
|
|
28
|
+
| Component | Description |
|
|
29
|
+
| --------------------- | ---------------------------------------------------------------------- |
|
|
30
|
+
| `ColourPicker` | Popover wrapper — renders swatch trigger and picker content in a panel |
|
|
31
|
+
| `ColourPickerContent` | Saturation/hue picker with hex input and preview swatch |
|
|
32
|
+
| `ColourSwatch` | Colour preview button sized `sm` (20px) or `md` (28px) |
|
|
33
|
+
| `ColourInput` | Hex colour text input wrapping `HexColorInput` from react-colorful |
|
|
34
|
+
|
|
35
|
+
## Props
|
|
36
|
+
|
|
37
|
+
### ColourPicker
|
|
38
|
+
|
|
39
|
+
| Prop | Type | Default | Description |
|
|
40
|
+
| ---------- | -------------------------- | ------- | -------------------------- |
|
|
41
|
+
| `value` | `string` | — | Current colour, in any CSS format (hex, `oklch()`, `rgb()`, named) |
|
|
42
|
+
| `onChange` | `(colour: string) => void` | — | Called with the new colour as hex |
|
|
43
|
+
| `onStart` | `() => void` | — | Called when a drag or edit begins. Useful for undo snapshots. |
|
|
44
|
+
| `children` | `React.ReactNode` | Swatch | Custom trigger element |
|
|
45
|
+
|
|
46
|
+
### ColourPickerContent
|
|
47
|
+
|
|
48
|
+
| Prop | Type | Default | Description |
|
|
49
|
+
| ---------- | -------------------------- | ------- | -------------------------- |
|
|
50
|
+
| `value` | `string` | — | Current colour, in any CSS format |
|
|
51
|
+
| `onChange` | `(colour: string) => void` | — | Called with the new colour as hex |
|
|
52
|
+
| `onStart` | `() => void` | — | Called when a drag or edit begins |
|
|
53
|
+
|
|
54
|
+
### ColourSwatch
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Default | Description |
|
|
57
|
+
| -------- | -------------- | ------- | --------------------- |
|
|
58
|
+
| `colour` | `string` | — | Any CSS colour to display |
|
|
59
|
+
| `size` | `"sm" \| "md"` | `"md"` | Swatch size |
|
|
60
|
+
|
|
61
|
+
### ColourInput
|
|
62
|
+
|
|
63
|
+
| Prop | Type | Default | Description |
|
|
64
|
+
| ---------- | -------------------------- | ------- | -------------------------- |
|
|
65
|
+
| `value` | `string` | — | Current hex colour value |
|
|
66
|
+
| `onChange` | `(colour: string) => void` | — | Called when colour changes |
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Command palette
|
|
3
|
+
description: Cmd+K search dialog for tools, actions, and keyboard shortcuts. Built on cmdk and Radix Dialog.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/command-palette
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Default
|
|
15
|
+
|
|
16
|
+
<ComponentPreview name="command-palette-demo" />
|
|
17
|
+
|
|
18
|
+
## Interaction
|
|
19
|
+
|
|
20
|
+
- **⌘K / Ctrl+K** opens and closes the palette.
|
|
21
|
+
- Type to fuzzy-search across all items.
|
|
22
|
+
- Arrow keys to navigate, Enter to select.
|
|
23
|
+
- Escape to dismiss.
|
|
24
|
+
|
|
25
|
+
## Components
|
|
26
|
+
|
|
27
|
+
| Component | Description |
|
|
28
|
+
| ------------------ | --------------------------------------- |
|
|
29
|
+
| `CommandPalette` | Dialog wrapper with ⌘K listener |
|
|
30
|
+
| `CommandInput` | Search input with magnifying glass icon |
|
|
31
|
+
| `CommandList` | Scrollable results container |
|
|
32
|
+
| `CommandEmpty` | Shown when no results match |
|
|
33
|
+
| `CommandGroup` | Labelled group of items |
|
|
34
|
+
| `CommandItem` | Selectable result row |
|
|
35
|
+
| `CommandShortcut` | Right-aligned keyboard shortcut hint |
|
|
36
|
+
| `CommandSeparator` | Horizontal divider between groups |
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Context menu
|
|
3
|
+
description: Right-click menu built on Radix ContextMenu with keyboard shortcut display and submenus.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/context-menu
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### Default
|
|
15
|
+
|
|
16
|
+
<ComponentPreview name="context-menu-demo" />
|
|
17
|
+
|
|
18
|
+
## Components
|
|
19
|
+
|
|
20
|
+
| Component | Description |
|
|
21
|
+
| ----------------------- | ------------------------------------- |
|
|
22
|
+
| `ContextMenu` | Root wrapper (Radix ContextMenu.Root) |
|
|
23
|
+
| `ContextMenuTrigger` | Area that responds to right-click |
|
|
24
|
+
| `ContextMenuContent` | Popover menu panel |
|
|
25
|
+
| `ContextMenuItem` | Clickable menu item |
|
|
26
|
+
| `ContextMenuSeparator` | Horizontal divider |
|
|
27
|
+
| `ContextMenuLabel` | Non-interactive section heading |
|
|
28
|
+
| `ContextMenuShortcut` | Right-aligned keyboard shortcut hint |
|
|
29
|
+
| `ContextMenuSub` | Submenu root |
|
|
30
|
+
| `ContextMenuSubTrigger` | Item that opens a submenu |
|
|
31
|
+
| `ContextMenuSubContent` | Submenu panel |
|