@json-to-office/mcp-server 1.4.0 → 1.5.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 +4 -1
- package/dist/cli.js +160 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +160 -14
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { McpServer, McpServerFactory, JsonSchemaType, StandardSchemaWithJSON } f
|
|
|
2
2
|
import { FormatAdapter, FormatName } from '@json-to-office/jto-ops';
|
|
3
3
|
export { FormatAdapter, FormatName } from '@json-to-office/jto-ops';
|
|
4
4
|
import { ValidationError } from '@json-to-office/shared';
|
|
5
|
+
import { QualityCategory, QualityCertainty, QualityEvidence, JsonPatchOperation as JsonPatchOperation$1 } from '@json-to-office/quality';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Structured results, not protocol errors.
|
|
@@ -32,6 +33,14 @@ interface Diagnostic {
|
|
|
32
33
|
suggestion?: string;
|
|
33
34
|
/** Free-form extras (offending value, component name, renderer id, …). */
|
|
34
35
|
context?: Record<string, unknown>;
|
|
36
|
+
source?: 'schema' | 'semantic' | 'renderer' | 'quality';
|
|
37
|
+
ruleId?: string;
|
|
38
|
+
category?: QualityCategory;
|
|
39
|
+
certainty?: QualityCertainty;
|
|
40
|
+
relatedPaths?: readonly string[];
|
|
41
|
+
evidence?: QualityEvidence;
|
|
42
|
+
fixes?: readonly JsonPatchOperation$1[];
|
|
43
|
+
blocking?: boolean;
|
|
35
44
|
}
|
|
36
45
|
/**
|
|
37
46
|
* The envelope every tool's `structuredContent` starts from.
|
|
@@ -100,6 +109,8 @@ declare const ERROR_CODES: {
|
|
|
100
109
|
readonly HOST_NOTE: "W_HOST_NOTE";
|
|
101
110
|
/** A generation warning the core raised without a code of its own. */
|
|
102
111
|
readonly GENERATION: "W_GENERATION";
|
|
112
|
+
/** A design-quality rule threw, so its whole class of findings is missing. */
|
|
113
|
+
readonly QUALITY_RULE_ERROR: "W_QUALITY_RULE_ERROR";
|
|
103
114
|
/** A required host binary (LibreOffice, poppler) is absent. */
|
|
104
115
|
readonly DEPENDENCY_MISSING: "E_DEPENDENCY_MISSING";
|
|
105
116
|
/** The client cancelled the request. */
|
|
@@ -177,6 +188,10 @@ declare const OPTION_ERROR_CODES: {
|
|
|
177
188
|
readonly INVALID_THEME_PATH: "E_INVALID_THEME_PATH";
|
|
178
189
|
/** The tool does not support the requested format. */
|
|
179
190
|
readonly UNSUPPORTED_FORMAT: "E_UNSUPPORTED_FORMAT";
|
|
191
|
+
/** `quality.profile` does not cover the format or renderer of this run. */
|
|
192
|
+
readonly INVALID_QUALITY_PROFILE: "E_INVALID_QUALITY_PROFILE";
|
|
193
|
+
/** `quality.policy` sets a gate, severity or budget that is not a legal value. */
|
|
194
|
+
readonly INVALID_QUALITY_POLICY: "E_INVALID_QUALITY_POLICY";
|
|
180
195
|
};
|
|
181
196
|
/**
|
|
182
197
|
* Normalize a validator path to an RFC 6901 JSON Pointer.
|
|
@@ -492,7 +507,7 @@ declare function createToolDeps(options?: CreateToolDepsOptions): ToolDeps;
|
|
|
492
507
|
* lose more than they fix, and that looking at a rendered page is cheaper than
|
|
493
508
|
* reasoning about whether a layout worked (#271).
|
|
494
509
|
*/
|
|
495
|
-
declare const SERVER_INSTRUCTIONS = "Author Microsoft Word (.docx) and PowerPoint (.pptx) documents as JSON.\n\nThe JSON is authoritative. A generated file is a build product of the document JSON plus a renderer, a theme, fonts, assets and options \u2014 edit the JSON and regenerate; never treat the binary as the source.\n\nWorking rules:\n- Discover before authoring. Call jto_info first, then jto_discover and jto_describe_component (or read the jto:// resources) for the components and renderer ids a format actually supports.\n- Make small edits. With a workspace handle, patch precisely (RFC 6902 over RFC 6901 paths) instead of resending the whole document; without one, change one region at a time.\n- Validate often. Run jto_validate after each edit rather than once at the end; diagnostics are path-addressed, so they map straight back onto the JSON you just changed.\n- Preview when the answer is visual. jto_preview renders pages to PNG; use it whenever layout, overflow or fit is in question, not only before finishing.\n- Snapshot before risky changes. jto_workspace_snapshot pins the current revision so a restructuring you cannot cleanly undo is still recoverable.\n\nDocument defects come back as structured diagnostics with ok: false, not as errors \u2014 read them and repair. Generated files are written under the server's output root and returned as paths; ask for base64 only for small artifacts.";
|
|
510
|
+
declare const SERVER_INSTRUCTIONS = "Author Microsoft Word (.docx) and PowerPoint (.pptx) documents as JSON.\n\nThe JSON is authoritative. A generated file is a build product of the document JSON plus a renderer, a theme, fonts, assets and options \u2014 edit the JSON and regenerate; never treat the binary as the source.\n\nWorking rules:\n- Discover before authoring. Call jto_info first, then jto_discover and jto_describe_component (or read the jto:// resources) for the components and renderer ids a format actually supports.\n- Make small edits. With a workspace handle, patch precisely (RFC 6902 over RFC 6901 paths) instead of resending the whole document; without one, change one region at a time.\n- Validate often. Run jto_validate after each edit rather than once at the end; diagnostics are path-addressed, so they map straight back onto the JSON you just changed.\n- Treat design findings as defects. Schema-valid is not well-designed: jto_validate also lints layout and legibility (W_QUALITY_* \u2014 undeclared slide canvas, text overflowing its box, overcrowded slides, table widths exceeding their section). These never block generation, but they almost always show in the rendered result \u2014 repair them like errors.\n- Preview when the answer is visual. jto_preview renders pages to PNG; use it whenever layout, overflow or fit is in question, not only before finishing.\n- Snapshot before risky changes. jto_workspace_snapshot pins the current revision so a restructuring you cannot cleanly undo is still recoverable.\n\nDocument defects come back as structured diagnostics with ok: false, not as errors \u2014 read them and repair. Generated files are written under the server's output root and returned as paths; ask for base64 only for small artifacts.";
|
|
496
511
|
/** Build a server with every tool and resource registered. */
|
|
497
512
|
declare function createServer(deps: ToolDeps): McpServer;
|
|
498
513
|
/**
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
3
3
|
|
|
4
4
|
// src/lib/version.ts
|
|
5
|
-
var SERVER_VERSION = true ? "1.
|
|
5
|
+
var SERVER_VERSION = true ? "1.5.0" : "dev-mode";
|
|
6
6
|
var SERVER_NAME = "json-to-office";
|
|
7
7
|
var PACKAGE_NAME = "@json-to-office/mcp-server";
|
|
8
8
|
|
|
@@ -216,6 +216,8 @@ var ERROR_CODES = {
|
|
|
216
216
|
HOST_NOTE: "W_HOST_NOTE",
|
|
217
217
|
/** A generation warning the core raised without a code of its own. */
|
|
218
218
|
GENERATION: "W_GENERATION",
|
|
219
|
+
/** A design-quality rule threw, so its whole class of findings is missing. */
|
|
220
|
+
QUALITY_RULE_ERROR: "W_QUALITY_RULE_ERROR",
|
|
219
221
|
/** A required host binary (LibreOffice, poppler) is absent. */
|
|
220
222
|
DEPENDENCY_MISSING: "E_DEPENDENCY_MISSING",
|
|
221
223
|
/** The client cancelled the request. */
|
|
@@ -312,6 +314,25 @@ function toolResult(payload) {
|
|
|
312
314
|
};
|
|
313
315
|
}
|
|
314
316
|
var HOST_DEPENDENCY_ERRORS = /* @__PURE__ */ new Set([RENDERER_DEPENDENCY_MISSING]);
|
|
317
|
+
function qualityOptionDiagnostic(error) {
|
|
318
|
+
const code = qualityCallerCode(error);
|
|
319
|
+
if (code === void 0 || code === ERROR_CODES.INVALID_DOCUMENT) {
|
|
320
|
+
return void 0;
|
|
321
|
+
}
|
|
322
|
+
return diagnostic(
|
|
323
|
+
code,
|
|
324
|
+
error instanceof Error ? error.message : String(error)
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
function qualityCallerCode(error) {
|
|
328
|
+
const code = error?.code;
|
|
329
|
+
if (code === "QUALITY_PROFILE_INCOMPATIBLE")
|
|
330
|
+
return OPTION_ERROR_CODES.INVALID_QUALITY_PROFILE;
|
|
331
|
+
if (code === "QUALITY_POLICY_INVALID")
|
|
332
|
+
return OPTION_ERROR_CODES.INVALID_QUALITY_POLICY;
|
|
333
|
+
if (code === "QUALITY_GATE_FAILED") return ERROR_CODES.INVALID_DOCUMENT;
|
|
334
|
+
return void 0;
|
|
335
|
+
}
|
|
315
336
|
function stackAllowed() {
|
|
316
337
|
const flag = process.env.JTO_MCP_DEBUG_STACKS;
|
|
317
338
|
return flag === "1" || flag === "true";
|
|
@@ -362,7 +383,7 @@ async function guarded(body) {
|
|
|
362
383
|
return withHostNotes(result, notes);
|
|
363
384
|
} catch (error) {
|
|
364
385
|
const message2 = error instanceof Error ? error.message : String(error);
|
|
365
|
-
const code = error instanceof Error && HOST_DEPENDENCY_ERRORS.has(error.name) ? ERROR_CODES.DEPENDENCY_MISSING : ERROR_CODES.INTERNAL;
|
|
386
|
+
const code = qualityCallerCode(error) ?? (error instanceof Error && HOST_DEPENDENCY_ERRORS.has(error.name) ? ERROR_CODES.DEPENDENCY_MISSING : ERROR_CODES.INTERNAL);
|
|
366
387
|
return withHostNotes(
|
|
367
388
|
failure(code, message2, {
|
|
368
389
|
context: {
|
|
@@ -381,7 +402,11 @@ var OPTION_ERROR_CODES = {
|
|
|
381
402
|
/** `themePath` is not a data-only JSON theme path. */
|
|
382
403
|
INVALID_THEME_PATH: "E_INVALID_THEME_PATH",
|
|
383
404
|
/** The tool does not support the requested format. */
|
|
384
|
-
UNSUPPORTED_FORMAT: "E_UNSUPPORTED_FORMAT"
|
|
405
|
+
UNSUPPORTED_FORMAT: "E_UNSUPPORTED_FORMAT",
|
|
406
|
+
/** `quality.profile` does not cover the format or renderer of this run. */
|
|
407
|
+
INVALID_QUALITY_PROFILE: "E_INVALID_QUALITY_PROFILE",
|
|
408
|
+
/** `quality.policy` sets a gate, severity or budget that is not a legal value. */
|
|
409
|
+
INVALID_QUALITY_POLICY: "E_INVALID_QUALITY_POLICY"
|
|
385
410
|
};
|
|
386
411
|
var DEFERRED_TO_COMPILER = /* @__PURE__ */ new Set([
|
|
387
412
|
ERROR_CODES.UNSUPPORTED_RENDERER_FEATURE
|
|
@@ -421,6 +446,34 @@ function validationDiagnostics(errors) {
|
|
|
421
446
|
})
|
|
422
447
|
);
|
|
423
448
|
}
|
|
449
|
+
function qualityAnalysisDiagnostics(analysis) {
|
|
450
|
+
return analysis.diagnostics.map((diagnostic2) => ({
|
|
451
|
+
source: diagnostic2.source,
|
|
452
|
+
ruleId: diagnostic2.ruleId,
|
|
453
|
+
category: diagnostic2.category,
|
|
454
|
+
certainty: diagnostic2.certainty,
|
|
455
|
+
severity: diagnostic2.severity,
|
|
456
|
+
code: diagnostic2.code,
|
|
457
|
+
message: diagnostic2.message,
|
|
458
|
+
path: diagnostic2.path,
|
|
459
|
+
blocking: diagnostic2.blocking,
|
|
460
|
+
...diagnostic2.suggestion !== void 0 && {
|
|
461
|
+
suggestion: diagnostic2.suggestion
|
|
462
|
+
},
|
|
463
|
+
...diagnostic2.context !== void 0 && {
|
|
464
|
+
context: { ...diagnostic2.context }
|
|
465
|
+
},
|
|
466
|
+
...diagnostic2.relatedPaths !== void 0 && {
|
|
467
|
+
relatedPaths: diagnostic2.relatedPaths
|
|
468
|
+
},
|
|
469
|
+
...diagnostic2.evidence !== void 0 && {
|
|
470
|
+
evidence: { ...diagnostic2.evidence }
|
|
471
|
+
},
|
|
472
|
+
...diagnostic2.fixes !== void 0 && {
|
|
473
|
+
fixes: diagnostic2.fixes
|
|
474
|
+
}
|
|
475
|
+
}));
|
|
476
|
+
}
|
|
424
477
|
function looksLikeValidationErrors(value) {
|
|
425
478
|
return Array.isArray(value) && value.length > 0 && value.every(
|
|
426
479
|
(entry) => typeof entry === "object" && entry !== null && typeof entry.message === "string"
|
|
@@ -1117,10 +1170,10 @@ var STARTERS = [
|
|
|
1117
1170
|
id: "pptx-minimal",
|
|
1118
1171
|
format: "pptx",
|
|
1119
1172
|
title: "Minimal presentation",
|
|
1120
|
-
description: "The smallest
|
|
1173
|
+
description: "The smallest well-formed .pptx: root with a declared 16:9 canvas, one slide, one title text. The canvas stays: without it the renderer silently falls back to 4:3.",
|
|
1121
1174
|
document: {
|
|
1122
1175
|
name: "pptx",
|
|
1123
|
-
props: { title: "Untitled deck" },
|
|
1176
|
+
props: { title: "Untitled deck", slideWidth: 13.333, slideHeight: 7.5 },
|
|
1124
1177
|
children: [
|
|
1125
1178
|
{
|
|
1126
1179
|
name: "slide",
|
|
@@ -1994,17 +2047,26 @@ function capDiagnostics(diagnostics, limit) {
|
|
|
1994
2047
|
if (diagnostics.length <= limit)
|
|
1995
2048
|
return { kept: diagnostics, truncated: false };
|
|
1996
2049
|
const ordered = [...diagnostics].sort(
|
|
1997
|
-
(a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity]
|
|
2050
|
+
(a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity] || Number(b.blocking === true) - Number(a.blocking === true)
|
|
1998
2051
|
);
|
|
1999
2052
|
return { kept: ordered.slice(0, limit), truncated: true };
|
|
2000
2053
|
}
|
|
2054
|
+
function ruleErrorDiagnostics(analysis) {
|
|
2055
|
+
return analysis.ruleErrors.map(
|
|
2056
|
+
(entry) => diagnostic(
|
|
2057
|
+
ERROR_CODES.QUALITY_RULE_ERROR,
|
|
2058
|
+
`Quality rule "${entry.ruleId}" failed: ${entry.message}`,
|
|
2059
|
+
{ severity: "warning", source: "quality", ruleId: entry.ruleId }
|
|
2060
|
+
)
|
|
2061
|
+
);
|
|
2062
|
+
}
|
|
2001
2063
|
var DEFAULT_MAX_DIAGNOSTICS = 100;
|
|
2002
2064
|
function register4(server, deps) {
|
|
2003
2065
|
server.registerTool(
|
|
2004
2066
|
"jto_validate",
|
|
2005
2067
|
{
|
|
2006
2068
|
title: "Validate a document",
|
|
2007
|
-
description: "Check a document against its format schema and report every defect as a path-addressed diagnostic. Paths are RFC 6901 JSON Pointers into the document you passed, so they can be used directly as patch targets; codes are the stable `E_`/`W_` vocabulary
|
|
2069
|
+
description: "Check a document against its format schema and report every defect as a path-addressed diagnostic. Paths are RFC 6901 JSON Pointers into the document you passed, so they can be used directly as patch targets; codes are the stable `E_`/`W_` vocabulary. `ok` mirrors generation: schema and semantic errors block; design-quality `W_QUALITY_*` findings advise by default and block only when `quality.policy.gate` requests it. A broken document is a normal result with `ok: false`, never a protocol error.",
|
|
2008
2070
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
2009
2071
|
inputSchema: S({
|
|
2010
2072
|
type: "object",
|
|
@@ -2020,6 +2082,29 @@ function register4(server, deps) {
|
|
|
2020
2082
|
minimum: 1,
|
|
2021
2083
|
maximum: 1e3,
|
|
2022
2084
|
description: `Cap on returned diagnostics (default ${DEFAULT_MAX_DIAGNOSTICS}). Errors are kept ahead of warnings when the cap bites.`
|
|
2085
|
+
},
|
|
2086
|
+
quality: {
|
|
2087
|
+
type: "object",
|
|
2088
|
+
description: "Optional design profile plus per-run enforcement policy.",
|
|
2089
|
+
properties: {
|
|
2090
|
+
profile: {
|
|
2091
|
+
type: "object",
|
|
2092
|
+
properties: { id: { type: "string", minLength: 1 } },
|
|
2093
|
+
required: ["id"],
|
|
2094
|
+
additionalProperties: true
|
|
2095
|
+
},
|
|
2096
|
+
policy: {
|
|
2097
|
+
type: "object",
|
|
2098
|
+
properties: {
|
|
2099
|
+
gate: {
|
|
2100
|
+
type: "string",
|
|
2101
|
+
enum: ["none", "error", "warning", "info"]
|
|
2102
|
+
}
|
|
2103
|
+
},
|
|
2104
|
+
additionalProperties: true
|
|
2105
|
+
}
|
|
2106
|
+
},
|
|
2107
|
+
additionalProperties: false
|
|
2023
2108
|
}
|
|
2024
2109
|
},
|
|
2025
2110
|
required: ["format"],
|
|
@@ -2050,7 +2135,11 @@ function register4(server, deps) {
|
|
|
2050
2135
|
},
|
|
2051
2136
|
truncated: {
|
|
2052
2137
|
type: "boolean",
|
|
2053
|
-
description: "`diagnostics` was capped by `maxDiagnostics
|
|
2138
|
+
description: "`diagnostics` was capped, by `maxDiagnostics` or by the budget the quality policy set."
|
|
2139
|
+
},
|
|
2140
|
+
profileId: {
|
|
2141
|
+
type: "string",
|
|
2142
|
+
description: "The quality profile the design analysis ran under, when one applied."
|
|
2054
2143
|
}
|
|
2055
2144
|
})
|
|
2056
2145
|
)
|
|
@@ -2070,24 +2159,52 @@ function register4(server, deps) {
|
|
|
2070
2159
|
resolved.document,
|
|
2071
2160
|
args.renderer
|
|
2072
2161
|
);
|
|
2073
|
-
|
|
2162
|
+
let analysis;
|
|
2163
|
+
let qualityOption;
|
|
2164
|
+
if (adapter.analyzeQuality) {
|
|
2165
|
+
try {
|
|
2166
|
+
analysis = await adapter.analyzeQuality(resolved.document, {
|
|
2167
|
+
renderer: args.renderer,
|
|
2168
|
+
quality: args.quality
|
|
2169
|
+
});
|
|
2170
|
+
} catch (error) {
|
|
2171
|
+
const option = result.valid ? void 0 : qualityOptionDiagnostic(error);
|
|
2172
|
+
if (!option) throw error;
|
|
2173
|
+
qualityOption = option;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
const structural = [
|
|
2074
2177
|
...validationDiagnostics(result.errors),
|
|
2075
|
-
...unavailable2 ? [unavailable2] : []
|
|
2178
|
+
...unavailable2 ? [unavailable2] : [],
|
|
2179
|
+
...qualityOption ? [qualityOption] : []
|
|
2180
|
+
];
|
|
2181
|
+
const all = [
|
|
2182
|
+
...structural,
|
|
2183
|
+
...analysis ? [
|
|
2184
|
+
...qualityAnalysisDiagnostics(analysis),
|
|
2185
|
+
...ruleErrorDiagnostics(analysis)
|
|
2186
|
+
] : []
|
|
2076
2187
|
];
|
|
2077
2188
|
const counts = countDiagnostics(all);
|
|
2189
|
+
const blocked = countDiagnostics(structural).error > 0 || analysis?.blocked === true;
|
|
2078
2190
|
const { kept, truncated } = capDiagnostics(
|
|
2079
2191
|
all,
|
|
2080
2192
|
args.maxDiagnostics ?? DEFAULT_MAX_DIAGNOSTICS
|
|
2081
2193
|
);
|
|
2082
2194
|
return {
|
|
2083
|
-
ok:
|
|
2195
|
+
ok: !blocked,
|
|
2084
2196
|
diagnostics: kept,
|
|
2085
|
-
valid:
|
|
2197
|
+
valid: !blocked,
|
|
2086
2198
|
format: args.format,
|
|
2087
2199
|
...args.renderer !== void 0 && { renderer: args.renderer },
|
|
2088
2200
|
source: sourceSummary(resolved),
|
|
2089
2201
|
counts,
|
|
2090
|
-
|
|
2202
|
+
// The engine caps its own list under a policy budget, so a report
|
|
2203
|
+
// shortened there would otherwise come back reading complete.
|
|
2204
|
+
truncated: truncated || analysis?.truncated === true,
|
|
2205
|
+
...analysis?.profileId !== void 0 && {
|
|
2206
|
+
profileId: analysis.profileId
|
|
2207
|
+
}
|
|
2091
2208
|
};
|
|
2092
2209
|
})
|
|
2093
2210
|
)
|
|
@@ -5717,6 +5834,7 @@ var RESOURCE_URIS = {
|
|
|
5717
5834
|
catalog: "jto://catalog",
|
|
5718
5835
|
renderers: "jto://renderers",
|
|
5719
5836
|
themes: "jto://themes",
|
|
5837
|
+
themeValues: "jto://themes/values",
|
|
5720
5838
|
templates: "jto://templates",
|
|
5721
5839
|
documentSchema: (format) => `jto://schema/${format}/document`,
|
|
5722
5840
|
themeSchema: (format) => `jto://schema/${format}/theme`
|
|
@@ -5768,7 +5886,7 @@ function register9(server, deps) {
|
|
|
5768
5886
|
RESOURCE_URIS.themes,
|
|
5769
5887
|
{
|
|
5770
5888
|
title: "Built-in themes",
|
|
5771
|
-
description: "Theme names shipped with each format, usable as a document\u2019s props.theme or as the tools\u2019 theme option.",
|
|
5889
|
+
description: "Theme names shipped with each format, usable as a document\u2019s props.theme or as the tools\u2019 theme option. jto://themes/values carries what each name actually looks like.",
|
|
5772
5890
|
mimeType: JSON_MIME
|
|
5773
5891
|
},
|
|
5774
5892
|
async (uri) => {
|
|
@@ -5781,6 +5899,33 @@ function register9(server, deps) {
|
|
|
5781
5899
|
});
|
|
5782
5900
|
}
|
|
5783
5901
|
);
|
|
5902
|
+
server.registerResource(
|
|
5903
|
+
"theme-values",
|
|
5904
|
+
RESOURCE_URIS.themeValues,
|
|
5905
|
+
{
|
|
5906
|
+
title: "Built-in theme values",
|
|
5907
|
+
description: "The palette, fonts, style tables and component defaults behind every built-in theme name \u2014 what a document actually opts into with props.theme. A name alone cannot tell you whether a theme fits the brief; this can.",
|
|
5908
|
+
mimeType: JSON_MIME
|
|
5909
|
+
},
|
|
5910
|
+
async (uri) => {
|
|
5911
|
+
const formats = await Promise.all(
|
|
5912
|
+
FORMAT_NAMES.map(async (format) => {
|
|
5913
|
+
const adapter = deps.getAdapter(format);
|
|
5914
|
+
let themes;
|
|
5915
|
+
try {
|
|
5916
|
+
themes = adapter.getBuiltinThemeValues ? await adapter.getBuiltinThemeValues() : adapter.getBuiltinThemes();
|
|
5917
|
+
} catch {
|
|
5918
|
+
themes = adapter.getBuiltinThemes();
|
|
5919
|
+
}
|
|
5920
|
+
return {
|
|
5921
|
+
format,
|
|
5922
|
+
themes
|
|
5923
|
+
};
|
|
5924
|
+
})
|
|
5925
|
+
);
|
|
5926
|
+
return jsonContents(uri, { formats });
|
|
5927
|
+
}
|
|
5928
|
+
);
|
|
5784
5929
|
server.registerResource(
|
|
5785
5930
|
"templates",
|
|
5786
5931
|
RESOURCE_URIS.templates,
|
|
@@ -5829,6 +5974,7 @@ Working rules:
|
|
|
5829
5974
|
- Discover before authoring. Call jto_info first, then jto_discover and jto_describe_component (or read the jto:// resources) for the components and renderer ids a format actually supports.
|
|
5830
5975
|
- Make small edits. With a workspace handle, patch precisely (RFC 6902 over RFC 6901 paths) instead of resending the whole document; without one, change one region at a time.
|
|
5831
5976
|
- Validate often. Run jto_validate after each edit rather than once at the end; diagnostics are path-addressed, so they map straight back onto the JSON you just changed.
|
|
5977
|
+
- Treat design findings as defects. Schema-valid is not well-designed: jto_validate also lints layout and legibility (W_QUALITY_* \u2014 undeclared slide canvas, text overflowing its box, overcrowded slides, table widths exceeding their section). These never block generation, but they almost always show in the rendered result \u2014 repair them like errors.
|
|
5832
5978
|
- Preview when the answer is visual. jto_preview renders pages to PNG; use it whenever layout, overflow or fit is in question, not only before finishing.
|
|
5833
5979
|
- Snapshot before risky changes. jto_workspace_snapshot pins the current revision so a restructuring you cannot cleanly undo is still recoverable.
|
|
5834
5980
|
|