@sanity-labs/slides 0.0.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/README.md +241 -0
- package/SKILL.md +119 -0
- package/dist/cli.d.ts +38 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +386 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/components.d.ts +179 -0
- package/dist/core/components.d.ts.map +1 -0
- package/dist/core/components.js +40 -0
- package/dist/core/components.js.map +1 -0
- package/dist/core/fake-runtime.d.ts +138 -0
- package/dist/core/fake-runtime.d.ts.map +1 -0
- package/dist/core/fake-runtime.js +210 -0
- package/dist/core/fake-runtime.js.map +1 -0
- package/dist/core/font-resolver.d.ts +28 -0
- package/dist/core/font-resolver.d.ts.map +1 -0
- package/dist/core/font-resolver.js +30 -0
- package/dist/core/font-resolver.js.map +1 -0
- package/dist/core/geometry.d.ts +71 -0
- package/dist/core/geometry.d.ts.map +1 -0
- package/dist/core/geometry.js +44 -0
- package/dist/core/geometry.js.map +1 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +20 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/manifest.d.ts +123 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +43 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/op-translator-pptx.d.ts +150 -0
- package/dist/core/op-translator-pptx.d.ts.map +1 -0
- package/dist/core/op-translator-pptx.js +245 -0
- package/dist/core/op-translator-pptx.js.map +1 -0
- package/dist/core/pptx-runtime.d.ts +103 -0
- package/dist/core/pptx-runtime.d.ts.map +1 -0
- package/dist/core/pptx-runtime.js +405 -0
- package/dist/core/pptx-runtime.js.map +1 -0
- package/dist/core/reconciler.d.ts +113 -0
- package/dist/core/reconciler.d.ts.map +1 -0
- package/dist/core/reconciler.js +453 -0
- package/dist/core/reconciler.js.map +1 -0
- package/dist/core/runtime.d.ts +161 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +11 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/template.d.ts +32 -0
- package/dist/core/template.d.ts.map +1 -0
- package/dist/core/template.js +3 -0
- package/dist/core/template.js.map +1 -0
- package/dist/dev/auto-examples.d.ts +6 -0
- package/dist/dev/auto-examples.d.ts.map +1 -0
- package/dist/dev/auto-examples.js +79 -0
- package/dist/dev/auto-examples.js.map +1 -0
- package/dist/dev/bin/slides-dev.d.ts +3 -0
- package/dist/dev/bin/slides-dev.d.ts.map +1 -0
- package/dist/dev/bin/slides-dev.js +87 -0
- package/dist/dev/bin/slides-dev.js.map +1 -0
- package/dist/dev/bin/slides-dev.mjs +24 -0
- package/dist/dev/compose-deck.d.ts +18 -0
- package/dist/dev/compose-deck.d.ts.map +1 -0
- package/dist/dev/compose-deck.js +19 -0
- package/dist/dev/compose-deck.js.map +1 -0
- package/dist/dev/deck-viewer.d.ts +19 -0
- package/dist/dev/deck-viewer.d.ts.map +1 -0
- package/dist/dev/deck-viewer.js +237 -0
- package/dist/dev/deck-viewer.js.map +1 -0
- package/dist/dev/dev-server/client/entry.d.ts +2 -0
- package/dist/dev/dev-server/client/entry.d.ts.map +1 -0
- package/dist/dev/dev-server/client/entry.js +12 -0
- package/dist/dev/dev-server/client/entry.js.map +1 -0
- package/dist/dev/dev-server/output.d.ts +8 -0
- package/dist/dev/dev-server/output.d.ts.map +1 -0
- package/dist/dev/dev-server/output.js +32 -0
- package/dist/dev/dev-server/output.js.map +1 -0
- package/dist/dev/dev-server/server-only-stub.d.ts +7 -0
- package/dist/dev/dev-server/server-only-stub.d.ts.map +1 -0
- package/dist/dev/dev-server/server-only-stub.js +12 -0
- package/dist/dev/dev-server/server-only-stub.js.map +1 -0
- package/dist/dev/dev-server/start.d.ts +14 -0
- package/dist/dev/dev-server/start.d.ts.map +1 -0
- package/dist/dev/dev-server/start.js +135 -0
- package/dist/dev/dev-server/start.js.map +1 -0
- package/dist/dev/index.d.ts +5 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/lib/cn.d.ts +3 -0
- package/dist/dev/lib/cn.d.ts.map +1 -0
- package/dist/dev/lib/cn.js +3 -0
- package/dist/dev/lib/cn.js.map +1 -0
- package/dist/dev/slide-canvas.d.ts +12 -0
- package/dist/dev/slide-canvas.d.ts.map +1 -0
- package/dist/dev/slide-canvas.js +123 -0
- package/dist/dev/slide-canvas.js.map +1 -0
- package/dist/dev/styles.css +37 -0
- package/dist/dev/ui/icon-button.d.ts +12 -0
- package/dist/dev/ui/icon-button.d.ts.map +1 -0
- package/dist/dev/ui/icon-button.js +6 -0
- package/dist/dev/ui/icon-button.js.map +1 -0
- package/dist/dev/ui/kbd.d.ts +6 -0
- package/dist/dev/ui/kbd.d.ts.map +1 -0
- package/dist/dev/ui/kbd.js +4 -0
- package/dist/dev/ui/kbd.js.map +1 -0
- package/dist/dev/ui/text-button.d.ts +10 -0
- package/dist/dev/ui/text-button.d.ts.map +1 -0
- package/dist/dev/ui/text-button.js +6 -0
- package/dist/dev/ui/text-button.js.map +1 -0
- package/dist/dev/url-state.d.ts +7 -0
- package/dist/dev/url-state.d.ts.map +1 -0
- package/dist/dev/url-state.js +13 -0
- package/dist/dev/url-state.js.map +1 -0
- package/dist/dev/use-keyboard-nav.d.ts +17 -0
- package/dist/dev/use-keyboard-nav.d.ts.map +1 -0
- package/dist/dev/use-keyboard-nav.js +53 -0
- package/dist/dev/use-keyboard-nav.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/errors.d.ts +57 -0
- package/dist/mcp/errors.d.ts.map +1 -0
- package/dist/mcp/errors.js +44 -0
- package/dist/mcp/errors.js.map +1 -0
- package/dist/mcp/index.d.ts +29 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +29 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/naming.d.ts +37 -0
- package/dist/mcp/naming.d.ts.map +1 -0
- package/dist/mcp/naming.js +43 -0
- package/dist/mcp/naming.js.map +1 -0
- package/dist/mcp/render.d.ts +45 -0
- package/dist/mcp/render.d.ts.map +1 -0
- package/dist/mcp/render.js +77 -0
- package/dist/mcp/render.js.map +1 -0
- package/dist/mcp/schema.d.ts +54 -0
- package/dist/mcp/schema.d.ts.map +1 -0
- package/dist/mcp/schema.js +55 -0
- package/dist/mcp/schema.js.map +1 -0
- package/dist/mcp/server.d.ts +63 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +196 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/scaffold/index.d.ts +39 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +84 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/template-base/README.md +134 -0
- package/dist/scaffold/template-base/_gitignore +4 -0
- package/dist/scaffold/template-base/package.json +35 -0
- package/dist/scaffold/template-base/src/components/Cover.tsx +30 -0
- package/dist/scaffold/template-base/src/index.ts +27 -0
- package/dist/scaffold/template-base/src/preview.tsx +9 -0
- package/dist/scaffold/template-base/tsconfig.build.json +10 -0
- package/dist/scaffold/template-base/tsconfig.json +18 -0
- package/package.json +164 -0
- package/src/__tests__/fixtures/test-template/index.tsx +77 -0
- package/src/__tests__/pptx-mcp.test.ts +85 -0
- package/src/__tests__/pptx-smoke.test.ts +45 -0
- package/src/__tests__/preview.test.ts +28 -0
- package/src/cli.ts +426 -0
- package/src/core/__snapshots__/reconciler.test.ts.snap +320 -0
- package/src/core/components.test.ts +57 -0
- package/src/core/components.ts +196 -0
- package/src/core/fake-runtime.test.ts +174 -0
- package/src/core/fake-runtime.ts +302 -0
- package/src/core/font-resolver.ts +46 -0
- package/src/core/geometry.test.ts +58 -0
- package/src/core/geometry.ts +91 -0
- package/src/core/index.ts +69 -0
- package/src/core/manifest.test.ts +33 -0
- package/src/core/manifest.ts +150 -0
- package/src/core/op-translator-pptx.test.ts +204 -0
- package/src/core/op-translator-pptx.ts +365 -0
- package/src/core/pptx-runtime.test.ts +137 -0
- package/src/core/pptx-runtime.ts +504 -0
- package/src/core/reconciler.test.ts +644 -0
- package/src/core/reconciler.ts +603 -0
- package/src/core/runtime.ts +150 -0
- package/src/core/template.test.ts +136 -0
- package/src/core/template.ts +37 -0
- package/src/dev/auto-examples.ts +89 -0
- package/src/dev/bin/slides-dev.mjs +24 -0
- package/src/dev/bin/slides-dev.ts +101 -0
- package/src/dev/compose-deck.test.ts +68 -0
- package/src/dev/compose-deck.ts +40 -0
- package/src/dev/deck-viewer.tsx +677 -0
- package/src/dev/dev-server/client/entry.tsx +15 -0
- package/src/dev/dev-server/client/index.html +24 -0
- package/src/dev/dev-server/output.ts +37 -0
- package/src/dev/dev-server/server-only-stub.ts +12 -0
- package/src/dev/dev-server/start.ts +155 -0
- package/src/dev/index.ts +4 -0
- package/src/dev/lib/cn.ts +3 -0
- package/src/dev/slide-canvas.test.tsx +66 -0
- package/src/dev/slide-canvas.tsx +170 -0
- package/src/dev/styles.css +37 -0
- package/src/dev/ui/icon-button.tsx +31 -0
- package/src/dev/ui/kbd.tsx +20 -0
- package/src/dev/ui/text-button.tsx +31 -0
- package/src/dev/url-state.test.ts +22 -0
- package/src/dev/url-state.ts +17 -0
- package/src/dev/use-keyboard-nav.ts +64 -0
- package/src/index.ts +17 -0
- package/src/mcp/errors.test.ts +51 -0
- package/src/mcp/errors.ts +76 -0
- package/src/mcp/index.ts +45 -0
- package/src/mcp/naming.test.ts +39 -0
- package/src/mcp/naming.ts +49 -0
- package/src/mcp/render.ts +110 -0
- package/src/mcp/schema.test.ts +86 -0
- package/src/mcp/schema.ts +93 -0
- package/src/mcp/server.test.ts +309 -0
- package/src/mcp/server.ts +276 -0
- package/src/scaffold/index.ts +102 -0
- package/src/scaffold/template-base/README.md +134 -0
- package/src/scaffold/template-base/_gitignore +4 -0
- package/src/scaffold/template-base/package.json +35 -0
- package/src/scaffold/template-base/src/components/Cover.tsx +30 -0
- package/src/scaffold/template-base/src/index.ts +27 -0
- package/src/scaffold/template-base/src/preview.tsx +9 -0
- package/src/scaffold/template-base/tsconfig.build.json +10 -0
- package/src/scaffold/template-base/tsconfig.json +18 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/mcp/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,eAAe,EAA0C,MAAM,oBAAoB,CAAC;AAI7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA2BhD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,aAAqB,EACrB,SAA4B,EAC5B,UAAmB,EACN,EAAE;IACf,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC;IAC1C,MAAM,eAAe,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC/E,OAAO;QACL,IAAI,EAAE,iBAAiB,CAAC,aAAa,EAAE,UAAU,CAAC;QAClD,WAAW,EAAE,SAAS,CAAC,WAAW;QAClC,UAAU;QACV,WAAW,EAAE,SAAS,CAAC,MAAM;QAC7B,eAAe;QACf,aAAa;KACd,CAAC;AACJ,CAAC,CAAC;AAEF,oDAAoD;AACpD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAe,EAAE,UAAmB,EAAiB,EAAE,CAC1F,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,CACzD,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAC7C,CAAC;AAEJ;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GAAoC;IAC3D,MAAM,EAAE,aAAa;IACrB,YAAY,EAAE,MAAM;CACrB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The template-agnostic MCP server framework.
|
|
3
|
+
*
|
|
4
|
+
* createSlideServer({ template, runtime }) builds an McpServer that exposes
|
|
5
|
+
* three kinds of tools:
|
|
6
|
+
*
|
|
7
|
+
* 1. **`slides_list`** — returns the list of slide types the loaded template
|
|
8
|
+
* supports, with descriptions. One round-trip to discover the surface.
|
|
9
|
+
*
|
|
10
|
+
* 2. **One tool per slide type**, named `<toolPrefix><snake_case>` (default
|
|
11
|
+
* prefix `slides_add_`). Validates user-supplied props against the
|
|
12
|
+
* component's Zod schema and echoes them back as a slide spec. Used
|
|
13
|
+
* iteratively by an LLM to assemble a deck before calling `slides_create`.
|
|
14
|
+
*
|
|
15
|
+
* 3. **`slides_create`** — full pipeline. Takes `{ title, slides }`, renders
|
|
16
|
+
* via the reconciler, applies through the PPTX runtime, writes the .pptx
|
|
17
|
+
* file to disk, and returns the path.
|
|
18
|
+
*/
|
|
19
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
20
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
21
|
+
import type { SlidesRuntime, Template } from '../core/index.js';
|
|
22
|
+
import { type DerivedTool } from './schema.js';
|
|
23
|
+
/** Configuration accepted by createSlideServer. */
|
|
24
|
+
export interface SlideServerConfig {
|
|
25
|
+
/** The template (component vocabulary + tokens) the server exposes. */
|
|
26
|
+
readonly template: Template;
|
|
27
|
+
/** The PPTX runtime used to materialize presentations. */
|
|
28
|
+
readonly runtime: SlidesRuntime;
|
|
29
|
+
/**
|
|
30
|
+
* Override the server's reported name/version. Defaults to a name derived
|
|
31
|
+
* from the template and version `'0.1.0'`.
|
|
32
|
+
*/
|
|
33
|
+
readonly serverInfo?: {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly version: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Override the per-slide-type tool-name prefix. Default: `'slides_add_'`.
|
|
39
|
+
* Use this when the server emits something other than slide decks (a
|
|
40
|
+
* report-builder might use `'report_add_'`, etc.).
|
|
41
|
+
*/
|
|
42
|
+
readonly toolPrefix?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Options accepted by server.start. */
|
|
45
|
+
export type StartOptions = {
|
|
46
|
+
readonly transport: 'stdio';
|
|
47
|
+
};
|
|
48
|
+
/** The handle returned by createSlideServer. */
|
|
49
|
+
export interface SlideServer {
|
|
50
|
+
/** The underlying MCP server. Exposed for advanced callers. */
|
|
51
|
+
readonly mcp: McpServer;
|
|
52
|
+
/** Tool definitions derived from the template. */
|
|
53
|
+
readonly tools: ReadonlyArray<DerivedTool>;
|
|
54
|
+
/** Connect to the given transport. Lower-level than start. */
|
|
55
|
+
connect(transport: Transport): Promise<void>;
|
|
56
|
+
/** Start serving over the configured transport. */
|
|
57
|
+
start(options: StartOptions): Promise<void>;
|
|
58
|
+
/** Close the server and disconnect any active transport. */
|
|
59
|
+
close(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
/** Construct a template-locked MCP server. */
|
|
62
|
+
export declare const createSlideServer: (config: SlideServerConfig) => SlideServer;
|
|
63
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAG/E,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAErE,mDAAmD;AACnD,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3D,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC3C,8DAA8D;IAC9D,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,mDAAmD;IACnD,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,4DAA4D;IAC5D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,8CAA8C;AAC9C,eAAO,MAAM,iBAAiB,GAAI,QAAQ,iBAAiB,KAAG,WA4B7D,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The template-agnostic MCP server framework.
|
|
3
|
+
*
|
|
4
|
+
* createSlideServer({ template, runtime }) builds an McpServer that exposes
|
|
5
|
+
* three kinds of tools:
|
|
6
|
+
*
|
|
7
|
+
* 1. **`slides_list`** — returns the list of slide types the loaded template
|
|
8
|
+
* supports, with descriptions. One round-trip to discover the surface.
|
|
9
|
+
*
|
|
10
|
+
* 2. **One tool per slide type**, named `<toolPrefix><snake_case>` (default
|
|
11
|
+
* prefix `slides_add_`). Validates user-supplied props against the
|
|
12
|
+
* component's Zod schema and echoes them back as a slide spec. Used
|
|
13
|
+
* iteratively by an LLM to assemble a deck before calling `slides_create`.
|
|
14
|
+
*
|
|
15
|
+
* 3. **`slides_create`** — full pipeline. Takes `{ title, slides }`, renders
|
|
16
|
+
* via the reconciler, applies through the PPTX runtime, writes the .pptx
|
|
17
|
+
* file to disk, and returns the path.
|
|
18
|
+
*/
|
|
19
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
20
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
import { errorResult, zodErrorResult } from './errors.js';
|
|
23
|
+
import { renderSlides } from './render.js';
|
|
24
|
+
import { deriveComponentTools } from './schema.js';
|
|
25
|
+
/** Construct a template-locked MCP server. */
|
|
26
|
+
export const createSlideServer = (config) => {
|
|
27
|
+
const { template, runtime } = config;
|
|
28
|
+
const serverInfo = config.serverInfo ?? {
|
|
29
|
+
name: 'react-pptx-mcp:' + template.name,
|
|
30
|
+
version: '0.1.0',
|
|
31
|
+
};
|
|
32
|
+
const mcp = new McpServer(serverInfo);
|
|
33
|
+
const tools = deriveComponentTools(template, config.toolPrefix);
|
|
34
|
+
registerListTool(mcp, template, tools);
|
|
35
|
+
registerComponentTools(mcp, tools);
|
|
36
|
+
registerCreateTool(mcp, runtime, template);
|
|
37
|
+
return {
|
|
38
|
+
mcp,
|
|
39
|
+
tools,
|
|
40
|
+
connect: (transport) => mcp.connect(transport),
|
|
41
|
+
start: async (options) => {
|
|
42
|
+
switch (options.transport) {
|
|
43
|
+
case 'stdio': {
|
|
44
|
+
await mcp.connect(new StdioServerTransport());
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
close: () => mcp.close(),
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// slides_list
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
const LIST_OUTPUT_SHAPE = {
|
|
56
|
+
template: z.string().describe('The name of the loaded template.'),
|
|
57
|
+
slides: z
|
|
58
|
+
.array(z.object({
|
|
59
|
+
name: z.string().describe('Slide-type name, e.g. "Cover".'),
|
|
60
|
+
toolName: z.string().describe('MCP tool name for this slide type.'),
|
|
61
|
+
description: z.string().describe('When to use this slide type.'),
|
|
62
|
+
}))
|
|
63
|
+
.describe('Every slide type the template exposes.'),
|
|
64
|
+
};
|
|
65
|
+
const registerListTool = (mcp, template, tools) => {
|
|
66
|
+
mcp.registerTool('slides_list', {
|
|
67
|
+
description: 'List every slide type this template supports, with descriptions and the per-type tool names. ' +
|
|
68
|
+
'Call once at the start of a deck-building session to learn the surface.',
|
|
69
|
+
outputSchema: LIST_OUTPUT_SHAPE,
|
|
70
|
+
annotations: {
|
|
71
|
+
readOnlyHint: true,
|
|
72
|
+
destructiveHint: false,
|
|
73
|
+
idempotentHint: true,
|
|
74
|
+
openWorldHint: false,
|
|
75
|
+
},
|
|
76
|
+
}, async () => {
|
|
77
|
+
const slides = tools.map((t) => ({
|
|
78
|
+
name: t.componentName,
|
|
79
|
+
toolName: t.name,
|
|
80
|
+
description: t.description,
|
|
81
|
+
}));
|
|
82
|
+
const lines = [
|
|
83
|
+
`Template: ${template.name}`,
|
|
84
|
+
'',
|
|
85
|
+
'Available slide types:',
|
|
86
|
+
...slides.map((s) => ` • ${s.name} (${s.toolName}) — ${s.description}`),
|
|
87
|
+
'',
|
|
88
|
+
'Call slides_add_<type> to validate a single slide. ' +
|
|
89
|
+
'Call slides_create with an array of slide specs to write a .pptx file.',
|
|
90
|
+
].join('\n');
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: 'text', text: lines }],
|
|
93
|
+
structuredContent: { template: template.name, slides },
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// slides_add_<component>
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
const COMPONENT_OUTPUT_SHAPE = {
|
|
101
|
+
slide: z
|
|
102
|
+
.object({
|
|
103
|
+
component: z.string(),
|
|
104
|
+
props: z.record(z.unknown()),
|
|
105
|
+
})
|
|
106
|
+
.describe('A single validated slide spec, ready to be passed to slides_create.'),
|
|
107
|
+
};
|
|
108
|
+
const registerComponentTools = (mcp, tools) => {
|
|
109
|
+
for (const tool of tools) {
|
|
110
|
+
mcp.registerTool(tool.name, {
|
|
111
|
+
description: tool.description,
|
|
112
|
+
inputSchema: tool.inputShape,
|
|
113
|
+
outputSchema: COMPONENT_OUTPUT_SHAPE,
|
|
114
|
+
annotations: {
|
|
115
|
+
readOnlyHint: true,
|
|
116
|
+
destructiveHint: false,
|
|
117
|
+
idempotentHint: true,
|
|
118
|
+
openWorldHint: false,
|
|
119
|
+
},
|
|
120
|
+
}, async (rawProps) => {
|
|
121
|
+
const parsed = tool.inputSchema.safeParse(rawProps);
|
|
122
|
+
if (!parsed.success) {
|
|
123
|
+
return zodErrorResult(`Validation error in ${tool.name} props:`, parsed.error, "Refer to this tool's input schema and retry.");
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
content: [
|
|
127
|
+
{
|
|
128
|
+
type: 'text',
|
|
129
|
+
text: `Validated ${tool.componentName} props. ` +
|
|
130
|
+
`Pass { component: "${tool.componentName}", props: <these> } ` +
|
|
131
|
+
`as one entry of slides_create.slides.`,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
structuredContent: {
|
|
135
|
+
slide: { component: tool.componentName, props: parsed.data },
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// slides_create
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
const SLIDE_SPEC_SCHEMA = z
|
|
145
|
+
.object({
|
|
146
|
+
component: z.string().min(1).describe('The slide-type name, e.g. "Cover".'),
|
|
147
|
+
props: z
|
|
148
|
+
.record(z.unknown())
|
|
149
|
+
.describe("Props matching that slide type's input schema (see slides_add_<type>)."),
|
|
150
|
+
})
|
|
151
|
+
.describe('One slide to add. Same shape as the structuredContent of slides_add_<type>.');
|
|
152
|
+
const CREATE_INPUT_SHAPE = {
|
|
153
|
+
title: z.string().min(1).describe('Deck title — used as the .pptx filename stem.'),
|
|
154
|
+
slides: z.array(SLIDE_SPEC_SCHEMA).min(1).describe('The slides to write, in order.'),
|
|
155
|
+
};
|
|
156
|
+
const CREATE_OUTPUT_SHAPE = {
|
|
157
|
+
filePath: z.string().describe('Absolute path to the generated .pptx file.'),
|
|
158
|
+
slideCount: z.number().int().nonnegative(),
|
|
159
|
+
};
|
|
160
|
+
const registerCreateTool = (mcp, runtime, template) => {
|
|
161
|
+
mcp.registerTool('slides_create', {
|
|
162
|
+
description: 'Generate a template-locked .pptx presentation from a sequence of slide specs and write it to disk. ' +
|
|
163
|
+
'Returns the absolute file path. Each spec is { component, props } — ' +
|
|
164
|
+
'use the per-type slides_add_<type> tools first to discover schemas and validate props.',
|
|
165
|
+
inputSchema: CREATE_INPUT_SHAPE,
|
|
166
|
+
outputSchema: CREATE_OUTPUT_SHAPE,
|
|
167
|
+
annotations: {
|
|
168
|
+
readOnlyHint: false,
|
|
169
|
+
destructiveHint: false,
|
|
170
|
+
idempotentHint: false,
|
|
171
|
+
openWorldHint: false,
|
|
172
|
+
},
|
|
173
|
+
}, async (input) => {
|
|
174
|
+
const result = await renderSlides({
|
|
175
|
+
template,
|
|
176
|
+
runtime,
|
|
177
|
+
title: input.title,
|
|
178
|
+
slides: input.slides,
|
|
179
|
+
});
|
|
180
|
+
if (result.ok) {
|
|
181
|
+
const slideWord = result.slideCount === 1 ? 'slide' : 'slides';
|
|
182
|
+
return {
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: `Wrote ${result.slideCount} ${slideWord} to ${result.filePath}.`,
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
structuredContent: { filePath: result.filePath, slideCount: result.slideCount },
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const hint = result.code === 'validation_error' ? ' Fix the listed fields and retry slides_create.' : '';
|
|
193
|
+
return errorResult(result.code, result.message + hint, result.issues);
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic scaffolder. The `slidesctl scaffold <dir>` subcommand wraps
|
|
3
|
+
* this; the same API is exposed at `@sanity-labs/slides/scaffold` so it can
|
|
4
|
+
* be driven by other tooling.
|
|
5
|
+
*
|
|
6
|
+
* The scaffold stamps every file under `template-base/` into the target
|
|
7
|
+
* directory, applying `__NAME__` / `__IDENT__` substitutions and renaming
|
|
8
|
+
* the `_gitignore` placeholder back to `.gitignore`.
|
|
9
|
+
*/
|
|
10
|
+
export type ScaffoldOptions = {
|
|
11
|
+
/** Target directory; created if missing. Must be empty if it exists. */
|
|
12
|
+
readonly target: string;
|
|
13
|
+
/** Template name (used as the package name + Template `name` field). */
|
|
14
|
+
readonly name: string;
|
|
15
|
+
};
|
|
16
|
+
export type ScaffoldResult = {
|
|
17
|
+
readonly targetPath: string;
|
|
18
|
+
readonly fileCount: number;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Stamp the template-base into `target`, applying the substitutions.
|
|
22
|
+
*
|
|
23
|
+
* Throws if `target` exists and is non-empty.
|
|
24
|
+
*/
|
|
25
|
+
export declare const scaffoldTemplate: (options: ScaffoldOptions) => ScaffoldResult;
|
|
26
|
+
/**
|
|
27
|
+
* Convert a kebab-case template name into a camelCase JS identifier. Used
|
|
28
|
+
* as the `__IDENT__` substitution in stamped files (e.g. the exported
|
|
29
|
+
* `Template` const).
|
|
30
|
+
*/
|
|
31
|
+
export declare const toIdentifier: (name: string) => string;
|
|
32
|
+
/**
|
|
33
|
+
* Validate a template name. Returns an error message string when invalid,
|
|
34
|
+
* `undefined` when ok. Used by interactive prompts.
|
|
35
|
+
*/
|
|
36
|
+
export declare const validateName: (value: string) => string | undefined;
|
|
37
|
+
/** Infer a default template name from a target path. */
|
|
38
|
+
export declare const defaultName: (target: string) => string;
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scaffold/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,MAAM,MAAM,eAAe,GAAG;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,eAAe,KAAG,cAU3D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MACqB,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAMrD,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,KAAG,MAG5C,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic scaffolder. The `slidesctl scaffold <dir>` subcommand wraps
|
|
3
|
+
* this; the same API is exposed at `@sanity-labs/slides/scaffold` so it can
|
|
4
|
+
* be driven by other tooling.
|
|
5
|
+
*
|
|
6
|
+
* The scaffold stamps every file under `template-base/` into the target
|
|
7
|
+
* directory, applying `__NAME__` / `__IDENT__` substitutions and renaming
|
|
8
|
+
* the `_gitignore` placeholder back to `.gitignore`.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { dirname, join, resolve as resolvePath } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const TEMPLATE_BASE = resolvePath(HERE, 'template-base');
|
|
15
|
+
/**
|
|
16
|
+
* Stamp the template-base into `target`, applying the substitutions.
|
|
17
|
+
*
|
|
18
|
+
* Throws if `target` exists and is non-empty.
|
|
19
|
+
*/
|
|
20
|
+
export const scaffoldTemplate = (options) => {
|
|
21
|
+
const targetPath = resolvePath(process.cwd(), options.target);
|
|
22
|
+
if (existsSync(targetPath) && readdirSync(targetPath).length > 0) {
|
|
23
|
+
throw new Error(`Target directory "${targetPath}" already exists and is not empty.`);
|
|
24
|
+
}
|
|
25
|
+
copyTemplate(TEMPLATE_BASE, targetPath, {
|
|
26
|
+
__NAME__: options.name,
|
|
27
|
+
__IDENT__: toIdentifier(options.name),
|
|
28
|
+
});
|
|
29
|
+
return { targetPath, fileCount: countFiles(targetPath) };
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Convert a kebab-case template name into a camelCase JS identifier. Used
|
|
33
|
+
* as the `__IDENT__` substitution in stamped files (e.g. the exported
|
|
34
|
+
* `Template` const).
|
|
35
|
+
*/
|
|
36
|
+
export const toIdentifier = (name) => name.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
37
|
+
/**
|
|
38
|
+
* Validate a template name. Returns an error message string when invalid,
|
|
39
|
+
* `undefined` when ok. Used by interactive prompts.
|
|
40
|
+
*/
|
|
41
|
+
export const validateName = (value) => {
|
|
42
|
+
if (!value)
|
|
43
|
+
return 'Required';
|
|
44
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
45
|
+
return 'Must start with a letter or digit and contain only [a-z0-9-].';
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
};
|
|
49
|
+
/** Infer a default template name from a target path. */
|
|
50
|
+
export const defaultName = (target) => {
|
|
51
|
+
const last = (target ?? './my-template').split('/').filter(Boolean).pop() ?? 'my-template';
|
|
52
|
+
return last.replace(/[^a-z0-9-]/gi, '-').toLowerCase();
|
|
53
|
+
};
|
|
54
|
+
const copyTemplate = (src, dst, replacements) => {
|
|
55
|
+
mkdirSync(dst, { recursive: true });
|
|
56
|
+
for (const entry of readdirSync(src)) {
|
|
57
|
+
const srcEntry = join(src, entry);
|
|
58
|
+
const dstEntry = join(dst, denormaliseFilename(entry));
|
|
59
|
+
if (statSync(srcEntry).isDirectory()) {
|
|
60
|
+
copyTemplate(srcEntry, dstEntry, replacements);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
writeFileSync(dstEntry, applyReplacements(readFileSync(srcEntry, 'utf8'), replacements));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const denormaliseFilename = (name) => (name === '_gitignore' ? '.gitignore' : name);
|
|
67
|
+
const applyReplacements = (content, replacements) => {
|
|
68
|
+
let out = content;
|
|
69
|
+
for (const [from, to] of Object.entries(replacements))
|
|
70
|
+
out = out.split(from).join(to);
|
|
71
|
+
return out;
|
|
72
|
+
};
|
|
73
|
+
const countFiles = (dir) => {
|
|
74
|
+
let n = 0;
|
|
75
|
+
for (const entry of readdirSync(dir)) {
|
|
76
|
+
const full = join(dir, entry);
|
|
77
|
+
if (statSync(full).isDirectory())
|
|
78
|
+
n += countFiles(full);
|
|
79
|
+
else
|
|
80
|
+
n += 1;
|
|
81
|
+
}
|
|
82
|
+
return n;
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scaffold/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# **NAME**
|
|
2
|
+
|
|
3
|
+
A react-pptx template. The brand lives in JSX; an LLM (Claude, anything that
|
|
4
|
+
speaks MCP) drives it through the generic `react-pptx-mcp` server without
|
|
5
|
+
ever touching fonts, colors, or layout.
|
|
6
|
+
|
|
7
|
+
## Develop
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm install
|
|
11
|
+
pnpm dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Opens the hot-reloading viewer at <http://localhost:5173>. Edit anything
|
|
15
|
+
under `src/` and the page updates.
|
|
16
|
+
|
|
17
|
+
## Anatomy
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
src/
|
|
21
|
+
├── index.ts # Template value — tokens, fonts, components map
|
|
22
|
+
├── preview.tsx # Canonical slide order shown in the viewer
|
|
23
|
+
└── components/ # Slide components, each one a Zod schema + JSX
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The Zod schema on every component is what Claude sees as the tool input
|
|
27
|
+
schema. Field descriptions become the LLM-facing documentation. Drop a
|
|
28
|
+
new component into `src/components/` and `defineTemplateComponent({...})`
|
|
29
|
+
in `src/index.ts` — that's the entire authoring loop.
|
|
30
|
+
|
|
31
|
+
## Generate a deck from the CLI
|
|
32
|
+
|
|
33
|
+
After editing, build once and then invoke the generic CLI against your
|
|
34
|
+
template:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm build
|
|
38
|
+
echo '{
|
|
39
|
+
"title": "Q2 Review",
|
|
40
|
+
"slides": [{ "component": "Cover", "props": { "title": "Q2 Review" } }]
|
|
41
|
+
}' | pnpm generate --output ~/Desktop
|
|
42
|
+
# /Users/you/Desktop/Q2-Review.pptx
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`pnpm generate` is a thin wrapper around `react-pptx-mcp generate --template
|
|
46
|
+
./dist/index.js`.
|
|
47
|
+
|
|
48
|
+
## Plug this template into Claude Desktop
|
|
49
|
+
|
|
50
|
+
No global install. No per-template binary. Use `npx` so Claude pulls the
|
|
51
|
+
generic MCP server straight from npm.
|
|
52
|
+
|
|
53
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
54
|
+
(or the equivalent on your OS):
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"__NAME__-slides": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": [
|
|
62
|
+
"-y",
|
|
63
|
+
"react-pptx-mcp",
|
|
64
|
+
"serve",
|
|
65
|
+
"--template",
|
|
66
|
+
"/absolute/path/to/__NAME__/dist/index.js",
|
|
67
|
+
"--output",
|
|
68
|
+
"/Users/you/Desktop"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Restart Claude. Three tools appear, auto-derived from this template:
|
|
76
|
+
|
|
77
|
+
| Tool | Purpose |
|
|
78
|
+
| ------------------- | ---------------------------------------------------------------- |
|
|
79
|
+
| `slides_list` | Lists every slide type, with descriptions. |
|
|
80
|
+
| `slides_add_<type>` | Validates a single slide's props against the Zod schema. |
|
|
81
|
+
| `slides_create` | Takes `{ title, slides }`, writes the `.pptx`, returns the path. |
|
|
82
|
+
|
|
83
|
+
Ask Claude:
|
|
84
|
+
|
|
85
|
+
> "Make me a 5-slide Q2 review deck with this template."
|
|
86
|
+
|
|
87
|
+
Whenever you edit the template, run `pnpm build` and Claude will pick up
|
|
88
|
+
the changes on its next request.
|
|
89
|
+
|
|
90
|
+
### Want Claude to know the conventions up front?
|
|
91
|
+
|
|
92
|
+
Drop the bundled skill into your Claude project. Print it with:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx react-pptx-mcp skill
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
…and paste into your Claude project's knowledge sources.
|
|
99
|
+
|
|
100
|
+
## API in 30 seconds
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { defineTemplate, defineTemplateComponent, CANVAS_16_9 } from 'react-pptx';
|
|
104
|
+
import { Cover, CoverSchema } from './components/Cover.js';
|
|
105
|
+
import { preview } from './preview.js';
|
|
106
|
+
|
|
107
|
+
export const __IDENT__ = defineTemplate({
|
|
108
|
+
name: '__NAME__',
|
|
109
|
+
canvas: CANVAS_16_9,
|
|
110
|
+
fonts: { display: ['Inter'], body: ['Inter'], mono: ['IBM Plex Mono'] },
|
|
111
|
+
colors: {},
|
|
112
|
+
typography: {},
|
|
113
|
+
spacing: {},
|
|
114
|
+
components: {
|
|
115
|
+
Cover: defineTemplateComponent({
|
|
116
|
+
component: Cover,
|
|
117
|
+
schema: CoverSchema,
|
|
118
|
+
description: 'Use as the first slide of a deck.',
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
preview,
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Add a new component:
|
|
126
|
+
|
|
127
|
+
1. Create `src/components/MySlide.tsx` exporting a Zod schema and a React
|
|
128
|
+
component. Keep all styling token-locked through your tokens map.
|
|
129
|
+
2. Register it in `src/index.ts` under `components` with a clear
|
|
130
|
+
description (LLMs read it).
|
|
131
|
+
3. Add a sample call in `src/preview.tsx` so the viewer shows it.
|
|
132
|
+
|
|
133
|
+
Run `pnpm build` and the new component appears as `slides_add_my_slide` the
|
|
134
|
+
next time Claude talks to your MCP server.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__NAME__",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "A react-pptx template.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./src/index.ts",
|
|
8
|
+
"types": "./src/index.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"src"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "slides-dev",
|
|
15
|
+
"build": "tsc -p tsconfig.build.json",
|
|
16
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
17
|
+
"serve": "react-pptx-mcp serve --template ./dist/index.js",
|
|
18
|
+
"generate": "react-pptx-mcp generate --template ./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": "^19.0.0",
|
|
22
|
+
"react-pptx": "^0.0.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"zod": "^3.23.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^19.0.0",
|
|
29
|
+
"react": "19.2.5",
|
|
30
|
+
"react-pptx": "^0.0.0",
|
|
31
|
+
"react-pptx-dev": "^0.0.0",
|
|
32
|
+
"react-pptx-mcp": "^0.0.0",
|
|
33
|
+
"typescript": "^5.5.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import { Slide, Box, Text } from '../../../../core/index.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
export const CoverSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
title: z.string().min(1).describe('The deck title.'),
|
|
8
|
+
subtitle: z.string().optional().describe('Optional subtitle.'),
|
|
9
|
+
})
|
|
10
|
+
.strict();
|
|
11
|
+
|
|
12
|
+
type CoverProps = z.infer<typeof CoverSchema>;
|
|
13
|
+
|
|
14
|
+
export const Cover = ({ title, subtitle }: CoverProps): ReactElement => (
|
|
15
|
+
<Slide>
|
|
16
|
+
<Box rect={{ x: 0, y: 0, w: 960, h: 540 }} fill={{ kind: 'solid', color: '#0b0b0b' }} />
|
|
17
|
+
<Box rect={{ x: 60, y: 200, w: 800, h: 120 }}>
|
|
18
|
+
<Text textStyle={{ fontFamily: 'display', fontSize: 64, foregroundColor: '#ffffff' }}>
|
|
19
|
+
{title}
|
|
20
|
+
</Text>
|
|
21
|
+
</Box>
|
|
22
|
+
{subtitle ? (
|
|
23
|
+
<Box rect={{ x: 60, y: 320, w: 800, h: 40 }}>
|
|
24
|
+
<Text textStyle={{ fontFamily: 'body', fontSize: 20, foregroundColor: '#cccccc' }}>
|
|
25
|
+
{subtitle}
|
|
26
|
+
</Text>
|
|
27
|
+
</Box>
|
|
28
|
+
) : null}
|
|
29
|
+
</Slide>
|
|
30
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CANVAS_16_9, defineTemplate, defineTemplateComponent } from '../../../core/index.js';
|
|
2
|
+
import { Cover, CoverSchema } from './components/Cover.js';
|
|
3
|
+
import { preview } from './preview.js';
|
|
4
|
+
|
|
5
|
+
export const __IDENT__ = defineTemplate({
|
|
6
|
+
name: '__NAME__',
|
|
7
|
+
canvas: CANVAS_16_9,
|
|
8
|
+
fonts: {
|
|
9
|
+
display: ['Inter', 'Arial'],
|
|
10
|
+
body: ['Inter', 'Arial'],
|
|
11
|
+
mono: ['IBM Plex Mono', 'Courier New'],
|
|
12
|
+
},
|
|
13
|
+
colors: {
|
|
14
|
+
'fg.base': '#0b0b0b',
|
|
15
|
+
'bg.surface': '#ffffff',
|
|
16
|
+
},
|
|
17
|
+
typography: {},
|
|
18
|
+
spacing: { md: 12, lg: 24 },
|
|
19
|
+
components: {
|
|
20
|
+
Cover: defineTemplateComponent({
|
|
21
|
+
component: Cover,
|
|
22
|
+
schema: CoverSchema,
|
|
23
|
+
description: 'Use as the first slide of a deck. Sets the title.',
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
preview,
|
|
27
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import { Cover } from './components/Cover.js';
|
|
3
|
+
|
|
4
|
+
export const preview = (): ReactElement => (
|
|
5
|
+
<>
|
|
6
|
+
<Cover title="Hello, __NAME__" subtitle="A react-pptx template." />
|
|
7
|
+
<Cover title="Edit me" subtitle="src/preview.tsx" />
|
|
8
|
+
</>
|
|
9
|
+
);
|