kss-modern 1.7.0-beta.0 → 1.7.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,16 +3,13 @@ import process from 'node:process';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import fs from 'fs-extra';
5
5
  import { glob } from 'tinyglobby';
6
- import { l as logger, s as sanitizeSpecialCharacters, P as PUG_ERROR_OVERLAY_TAG, r as requireZod, e as evaluateMixinSchema, c as compileMixinModule, p as pugErrorFile, a as compileMarkup, g as getZodVersion, M as MIXIN_COMPILE_OPTIONS, b as getMixinWrapperSource, d as slugify, I as INSERT_VITE_PUG_TAG_RE, f as PUG_SRC_RE, t as themeClassList, h as ensureStartingSlash, i as sectionSanitizeId, j as stripPugErrorOverlay, k as generateId, m as replaceWrapperContent, n as formatSourceCode, o as htmlToSearchText } from '../shared/kss-modern.N3ElcapL.mjs';
7
- export { q as createLogger } from '../shared/kss-modern.N3ElcapL.mjs';
6
+ import { l as logger, s as sanitizeSpecialCharacters, P as PUG_ERROR_OVERLAY_TAG, r as requireZod, e as evaluateMixinSchema, c as compileMixinModule, p as pugErrorFile, a as compileMarkup, g as getZodVersion, d as discoverMixins, M as MIXIN_COMPILE_OPTIONS, b as getMixinWrapperSource, f as slugify, I as INSERT_VITE_PUG_TAG_RE, h as PUG_SRC_RE, t as themeClassList, i as ensureStartingSlash, j as sectionSanitizeId, k as stripPugErrorOverlay, m as generateId, n as replaceWrapperContent, o as formatSourceCode, q as htmlToSearchText } from '../shared/kss-modern.BOTbHnCm.mjs';
7
+ export { u as createLogger } from '../shared/kss-modern.BOTbHnCm.mjs';
8
8
  import { existsSync, readFileSync } from 'node:fs';
9
9
  import fs$1 from 'node:fs/promises';
10
10
  import { createHash } from 'node:crypto';
11
11
  import os from 'node:os';
12
12
  import { Worker } from 'node:worker_threads';
13
- import lex from 'pug-lexer';
14
- import parse$1 from 'pug-parser';
15
- import walk from 'pug-walk';
16
13
  import htmlLang from '@shikijs/langs/html';
17
14
  import markdownLang from '@shikijs/langs/markdown';
18
15
  import scssLang from '@shikijs/langs/scss';
@@ -30,6 +27,9 @@ import 'pug';
30
27
  import 'consola';
31
28
  import 'consola/utils';
32
29
  import 'node:module';
30
+ import 'pug-walk';
31
+ import 'pug-lexer';
32
+ import 'pug-parser';
33
33
  import 'pug-load';
34
34
 
35
35
  const HEX_COLOR_RE = /^#[0-9A-F]{6}$/i;
@@ -586,42 +586,6 @@ async function logicalWriteFile(filepath, content) {
586
586
  await fs.writeFile(filepath, content);
587
587
  }
588
588
 
589
- const MIXIN_SCHEMA_RE = /\bmixinSchema\s*=/;
590
- const MIXIN_NAME_RE = /^\s*(?:var|let|const)\s+mixinName\s*=\s*(['"])([^'"\n]*)\1\s*(?:;\s*)?$/;
591
- const MIXIN_KEYWORD_RE = /^\s*mixin\s/m;
592
- function findMixinCode(block) {
593
- const found = {};
594
- walk(block, (node) => {
595
- if (node.type === "Mixin")
596
- return false;
597
- if (node.type !== "Code" || node.buffer)
598
- return;
599
- if (found.schemaSource === void 0 && MIXIN_SCHEMA_RE.test(node.val))
600
- found.schemaSource = node.val;
601
- const name = MIXIN_NAME_RE.exec(node.val)?.[2];
602
- if (found.mixinName === void 0 && name !== void 0)
603
- found.mixinName = name;
604
- });
605
- return found;
606
- }
607
- function discoverMixins(source, filename) {
608
- if (!MIXIN_KEYWORD_RE.test(source))
609
- return [];
610
- const ast = parse$1(lex(source, { filename }), { filename, src: source });
611
- const mixins = [];
612
- for (const node of ast.nodes) {
613
- if (node.type !== "Mixin" || node.call)
614
- continue;
615
- mixins.push({
616
- name: node.name,
617
- args: node.args ?? null,
618
- line: node.line,
619
- ...node.block ? findMixinCode(node.block) : {}
620
- });
621
- }
622
- return mixins;
623
- }
624
-
625
589
  const MIXIN_MANIFEST_PATH = "mixins/manifest.json";
626
590
  const MIXIN_OVERVIEW_FILE = "mixins.html";
627
591
  function getMixinExplorerHref(id) {
@@ -1846,7 +1810,7 @@ async function generateFullPageFile(data) {
1846
1810
  <meta name="theme-color" media="(prefers-color-scheme: dark)" content="${data.brandColor.dark}">
1847
1811
  ` : `<meta name="theme-color" content="${data.brandColor}">`}
1848
1812
  <script type="module" src="styleguide-assets/fullpage-aWqc0gjm.js"><\/script>
1849
- ${data.playgroundConfig ? '<script type="module" src="styleguide-assets/playground-D9faUdLy.js"><\/script>' : ""}
1813
+ ${data.playgroundConfig ? '<script type="module" src="styleguide-assets/playground-BuO-B-Kx.js"><\/script>' : ""}
1850
1814
  ${computedStyleTags}
1851
1815
  ${computedThemeStyleTags}
1852
1816
  </head>
@@ -2957,7 +2921,7 @@ document.addEventListener("DOMContentLoaded", () => {
2957
2921
  //#endregion
2958
2922
  ;
2959
2923
  <\/script>
2960
- <script type="module" src="styleguide-assets/preview-CsX9psV8.js"><\/script>
2924
+ <script type="module" src="styleguide-assets/preview-C3WnTlhx.js"><\/script>
2961
2925
  <link rel="preload" href="styleguide-assets/fonts/geist-mono-latin-400-normal.woff2" as="font" type="font/woff2" crossorigin="anonymous">
2962
2926
  <link rel="preload" href="styleguide-assets/fonts/geist-mono-latin-600-normal.woff2" as="font" type="font/woff2" crossorigin="anonymous">
2963
2927
  <link rel="preload" href="styleguide-assets/fonts/geist-mono-latin-300-normal.woff2" as="font" type="font/woff2" crossorigin="anonymous">
@@ -1,5 +1,5 @@
1
1
  import { parentPort } from 'node:worker_threads';
2
- import { r as requireZod, e as evaluateMixinSchema, c as compileMixinModule, a as compileMarkup, p as pugErrorFile } from '../../shared/kss-modern.N3ElcapL.mjs';
2
+ import { r as requireZod, e as evaluateMixinSchema, c as compileMixinModule, a as compileMarkup, p as pugErrorFile } from '../../shared/kss-modern.BOTbHnCm.mjs';
3
3
  import 'node:path';
4
4
  import 'pug';
5
5
  import 'node:fs';
@@ -8,6 +8,8 @@ import 'consola';
8
8
  import 'consola/utils';
9
9
  import 'node:module';
10
10
  import 'pug-walk';
11
+ import 'pug-lexer';
12
+ import 'pug-parser';
11
13
  import 'pug-load';
12
14
 
13
15
  if (!parentPort) {
@@ -6,6 +6,8 @@ import { createConsola } from 'consola';
6
6
  import { colors } from 'consola/utils';
7
7
  import { createRequire } from 'node:module';
8
8
  import walk from 'pug-walk';
9
+ import lex from 'pug-lexer';
10
+ import parse from 'pug-parser';
9
11
  import pugLoad from 'pug-load';
10
12
 
11
13
  const sectionSanitizeId = (id2) => id2.toLowerCase().replaceAll(".", "-");
@@ -98,6 +100,72 @@ function generateId() {
98
100
  return value;
99
101
  }
100
102
 
103
+ const MIXIN_SCHEMA_RE = /\bmixinSchema\s*=/;
104
+ const MIXIN_NAME_RE = /^\s*(?:var|let|const)\s+mixinName\s*=\s*(['"])([^'"\n]*)\1\s*(?:;\s*)?$/;
105
+ const MIXIN_KEYWORD_RE = /^\s*mixin\s/m;
106
+ function findMixinCode(block) {
107
+ const found = {};
108
+ walk(block, (node) => {
109
+ if (node.type === "Mixin")
110
+ return false;
111
+ if (node.type !== "Code" || node.buffer)
112
+ return;
113
+ if (found.schemaSource === void 0 && MIXIN_SCHEMA_RE.test(node.val))
114
+ found.schemaSource = node.val;
115
+ const name = MIXIN_NAME_RE.exec(node.val)?.[2];
116
+ if (found.mixinName === void 0 && name !== void 0)
117
+ found.mixinName = name;
118
+ });
119
+ return found;
120
+ }
121
+ function discoverMixins(source, filename) {
122
+ if (!MIXIN_KEYWORD_RE.test(source))
123
+ return [];
124
+ const ast = parse(lex(source, { filename }), { filename, src: source });
125
+ const mixins = [];
126
+ for (const node of ast.nodes) {
127
+ if (node.type !== "Mixin" || node.call)
128
+ continue;
129
+ mixins.push({
130
+ name: node.name,
131
+ args: node.args ?? null,
132
+ line: node.line,
133
+ ...node.block ? findMixinCode(node.block) : {}
134
+ });
135
+ }
136
+ return mixins;
137
+ }
138
+
139
+ function getMixinMarkerName(name, mixinName) {
140
+ const segment = mixinName?.split("/").map((part) => part.trim()).filter(Boolean).at(-1) ?? name;
141
+ return segment.replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/[-_\s]+/g, " ").trim().toUpperCase();
142
+ }
143
+ function createComment(val, node) {
144
+ return { type: "Comment", val, buffer: true, line: node.line, column: node.column, filename: node.filename };
145
+ }
146
+ function hasStartMarker(nodes) {
147
+ return nodes.some((node) => node.type === "Comment" && node.buffer === true && node.val?.trim().startsWith("START "));
148
+ }
149
+ const mixinMarkerPlugin = {
150
+ preCodeGen(ast) {
151
+ return walk(ast, (node) => {
152
+ if (node.type !== "Mixin" || node.call || !node.block || typeof node.name !== "string")
153
+ return;
154
+ if (hasStartMarker(node.block.nodes))
155
+ return;
156
+ const { schemaSource, mixinName } = findMixinCode(node.block);
157
+ if (schemaSource === void 0)
158
+ return;
159
+ const markerName = getMixinMarkerName(node.name, mixinName);
160
+ node.block.nodes = [
161
+ createComment(` START ${markerName}`, node),
162
+ ...node.block.nodes,
163
+ createComment(` END ${markerName}`, node)
164
+ ];
165
+ });
166
+ }
167
+ };
168
+
101
169
  const parseCache = /* @__PURE__ */ new Map();
102
170
  function signatureOf(filename) {
103
171
  try {
@@ -178,7 +246,7 @@ function compileMixinModule(file, name) {
178
246
  ...MIXIN_COMPILE_OPTIONS,
179
247
  // a sibling of the mixin's file, so its relative includes resolve exactly as in the file itself
180
248
  filename: path.join(path.dirname(absoluteFile), `__kss-mixin-${name}.pug`),
181
- plugins: [mixinPugPlugin]
249
+ plugins: [mixinPugPlugin, mixinMarkerPlugin]
182
250
  });
183
251
  return {
184
252
  code: `${body}
@@ -195,6 +263,7 @@ const BOOLEAN_KEYS = ["type", "enum", "const", ...ANNOTATION_KEYS];
195
263
  const ARRAY_KEYS = ["type", "items", "minItems", "maxItems", ...ANNOTATION_KEYS];
196
264
  const LITERAL_UNION_KEYS = ["anyOf", ...ANNOTATION_KEYS];
197
265
  const ROOT_KEYS = ["$schema", "type", "properties", "required", "additionalProperties", ...ANNOTATION_KEYS];
266
+ const ENTRY_KEYS = ["type", "properties", "required", "additionalProperties", ...ANNOTATION_KEYS];
198
267
  const CHECKED_FORMATS = ["email", "uri"];
199
268
  function isObject(value) {
200
269
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -205,7 +274,19 @@ function isLiteral(value) {
205
274
  function checkKeys(schema, allowed) {
206
275
  return Object.keys(schema).filter((key) => !allowed.includes(key)).map((key) => `uses "${key}", which comes from a zod feature the playground does not support`);
207
276
  }
208
- function checkValue(schema, isArrayItem) {
277
+ function checkEntry(schema) {
278
+ if (!isObject(schema.properties))
279
+ return ["is an object without fields, which is not supported"];
280
+ const errors = checkKeys(schema, ENTRY_KEYS);
281
+ if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false)
282
+ errors.push("allows unknown keys (z.looseObject or .catchall()), which is not supported");
283
+ for (const [key, field] of Object.entries(schema.properties)) {
284
+ const fieldErrors = isObject(field) ? checkValue(field, "entry-field") : ["is not a schema"];
285
+ errors.push(...fieldErrors.map((error) => `field "${key}" ${error}`));
286
+ }
287
+ return errors;
288
+ }
289
+ function checkValue(schema, position) {
209
290
  if (Array.isArray(schema.anyOf)) {
210
291
  if (!schema.anyOf.every(isLiteral))
211
292
  return ["is a union, only unions of z.literal() values are supported"];
@@ -226,17 +307,23 @@ function checkValue(schema, isArrayItem) {
226
307
  case "boolean":
227
308
  return checkKeys(schema, BOOLEAN_KEYS);
228
309
  case "array": {
229
- if (isArrayItem)
230
- return ["is an array of arrays, only arrays of strings, numbers, booleans and enums are supported"];
310
+ if (position === "array-item")
311
+ return ["is an array of arrays, only arrays of scalars or of flat objects are supported"];
312
+ if (position === "entry-field")
313
+ return ["is an array, the fields of a list entry must be scalars"];
231
314
  if (!isObject(schema.items))
232
315
  return ["is an array without an item type (a tuple), which is not supported"];
233
316
  return [
234
317
  ...checkKeys(schema, ARRAY_KEYS),
235
- ...checkValue(schema.items, true).map((error) => `items ${error}`)
318
+ ...checkValue(schema.items, "array-item").map((error) => `items ${error}`)
236
319
  ];
237
320
  }
238
321
  case "object":
239
- return [isArrayItem ? "is an array of objects, only arrays of strings, numbers, booleans and enums are supported" : "is an object (or record), nested objects are not supported"];
322
+ if (position === "array-item")
323
+ return checkEntry(schema);
324
+ if (position === "entry-field")
325
+ return ["is an object, the fields of a list entry must be scalars"];
326
+ return ["is an object (or record), nested objects are not supported; use an array of objects for a list of entries"];
240
327
  default:
241
328
  return ["has a type the playground cannot edit (e.g. z.any() or z.unknown())"];
242
329
  }
@@ -248,7 +335,7 @@ function checkSchemaSupport(schema) {
248
335
  if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false)
249
336
  errors.push("the schema allows unknown keys (z.looseObject or .catchall()), which is not supported");
250
337
  for (const [key, property] of Object.entries(schema.properties)) {
251
- const propertyErrors = isObject(property) ? checkValue(property, false) : ["is not a schema"];
338
+ const propertyErrors = isObject(property) ? checkValue(property, "prop") : ["is not a schema"];
252
339
  errors.push(...propertyErrors.map((error) => `prop "${key}" ${error}`));
253
340
  }
254
341
  return errors;
@@ -417,8 +504,9 @@ function compilePugFile(pugFilePath, mode, sectionId, extraLocals = {}) {
417
504
  // line-mapping — measurably bloating codegen and `new Function`. We surface compile errors via
418
505
  // our own overlay, so the precise pug line in a runtime stack isn't worth the cost.
419
506
  compileDebug: false,
420
- // records which mixins the section calls, see ./mixin-trace.ts
421
- plugins: [mixinTracePlugin]
507
+ // START/END comments around public mixins, see ./mixin-markers.ts; records which mixins the
508
+ // section calls, see ./mixin-trace.ts
509
+ plugins: [mixinMarkerPlugin, mixinTracePlugin]
422
510
  });
423
511
  const dependencies = [path.resolve(pugFilePath), ...pugFn.dependencies.map((dep) => path.resolve(dep))];
424
512
  const locals = { modifierClass: "{{modifier_class}}", ...extraLocals, useId: createUseId(sectionId) };
@@ -480,4 +568,4 @@ async function compileMarkup(contentDir, mode, html, sectionId) {
480
568
  return { html: result, dependencies: [...new Set(dependencies)], mixinUsage: mergeMixinUsage(mixinUsage) };
481
569
  }
482
570
 
483
- export { INSERT_VITE_PUG_TAG_RE as I, MIXIN_COMPILE_OPTIONS as M, PUG_ERROR_OVERLAY_TAG as P, compileMarkup as a, getMixinWrapperSource as b, compileMixinModule as c, slugify as d, evaluateMixinSchema as e, PUG_SRC_RE as f, getZodVersion as g, ensureStartingSlash as h, sectionSanitizeId as i, stripPugErrorOverlay as j, generateId as k, logger as l, replaceWrapperContent as m, formatSourceCode as n, htmlToSearchText as o, pugErrorFile as p, createLogger as q, requireZod as r, sanitizeSpecialCharacters as s, themeClassList as t };
571
+ export { INSERT_VITE_PUG_TAG_RE as I, MIXIN_COMPILE_OPTIONS as M, PUG_ERROR_OVERLAY_TAG as P, compileMarkup as a, getMixinWrapperSource as b, compileMixinModule as c, discoverMixins as d, evaluateMixinSchema as e, slugify as f, getZodVersion as g, PUG_SRC_RE as h, ensureStartingSlash as i, sectionSanitizeId as j, stripPugErrorOverlay as k, logger as l, generateId as m, replaceWrapperContent as n, formatSourceCode as o, pugErrorFile as p, htmlToSearchText as q, requireZod as r, sanitizeSpecialCharacters as s, themeClassList as t, createLogger as u };
@@ -21,8 +21,8 @@
21
21
  "file": "code-highlight-Fm7cxf-E.js",
22
22
  "name": "code-highlight"
23
23
  },
24
- "_controller-BLn-0hDZ.js": {
25
- "file": "controller-BLn-0hDZ.js",
24
+ "_controller-BduQiyRp.js": {
25
+ "file": "controller-BduQiyRp.js",
26
26
  "name": "controller",
27
27
  "imports": [
28
28
  "_utils-DtB-AzJa.js",
@@ -147,14 +147,14 @@
147
147
  ]
148
148
  },
149
149
  "client/lib/playground/explorer.ts": {
150
- "file": "explorer-ADHDxFj8.js",
150
+ "file": "explorer-B2UH96jd.js",
151
151
  "name": "explorer",
152
152
  "src": "client/lib/playground/explorer.ts",
153
153
  "isDynamicEntry": true,
154
154
  "imports": [
155
155
  "_code-highlight-Fm7cxf-E.js",
156
156
  "_utils-DtB-AzJa.js",
157
- "_controller-BLn-0hDZ.js",
157
+ "_controller-BduQiyRp.js",
158
158
  "_alerts-DIQ8TeZB.js"
159
159
  ]
160
160
  },
@@ -169,13 +169,13 @@
169
169
  ]
170
170
  },
171
171
  "client/playground.ts": {
172
- "file": "playground-D9faUdLy.js",
172
+ "file": "playground-BuO-B-Kx.js",
173
173
  "name": "playground",
174
174
  "src": "client/playground.ts",
175
175
  "isEntry": true,
176
176
  "imports": [
177
177
  "_shared-hGcWlnRM.js",
178
- "_controller-BLn-0hDZ.js"
178
+ "_controller-BduQiyRp.js"
179
179
  ]
180
180
  },
181
181
  "client/preview-inline.ts": {
@@ -185,7 +185,7 @@
185
185
  "isEntry": true
186
186
  },
187
187
  "client/preview.ts": {
188
- "file": "preview-CsX9psV8.js",
188
+ "file": "preview-C3WnTlhx.js",
189
189
  "name": "preview",
190
190
  "src": "client/preview.ts",
191
191
  "isEntry": true,
@@ -64,13 +64,20 @@ function d(e) {
64
64
  hasDefault: "default" in r,
65
65
  hasExample: i.length > 0
66
66
  };
67
- return r.type === "array" ? {
68
- ...a,
69
- kind: "array",
70
- items: u(s(r.items) ? r.items : {}),
71
- minItems: c(r.minItems),
72
- maxItems: c(r.maxItems)
73
- } : {
67
+ if (r.type === "array") {
68
+ let e = s(r.items) ? r.items : {};
69
+ return {
70
+ ...a,
71
+ kind: "array",
72
+ items: e.type === "object" ? {
73
+ kind: "object",
74
+ fields: d(e)
75
+ } : u(e),
76
+ minItems: c(r.minItems),
77
+ maxItems: c(r.maxItems)
78
+ };
79
+ }
80
+ return {
74
81
  ...a,
75
82
  ...u(r)
76
83
  };
@@ -86,99 +93,131 @@ function f(e) {
86
93
  }
87
94
  }
88
95
  function p(e) {
96
+ if (e.kind !== "object") return f(e);
97
+ let t = {};
98
+ for (let n of e.fields) n.hasDefault ? t[n.key] = n.default : n.isOptional || (t[n.key] = f(n));
99
+ return t;
100
+ }
101
+ function m(e) {
89
102
  if (e.hasDefault) return e.default;
90
103
  if (e.hasExample) return e.example;
91
104
  if (!e.isOptional) return e.kind === "array" ? [] : f(e);
92
105
  }
93
- function m(e, t) {
94
- return Object.fromEntries(e.map((e) => [e.key, t ? t[e.key] : p(e)]));
95
- }
96
106
  function h(e, t) {
107
+ return Object.fromEntries(e.map((e) => [e.key, t ? t[e.key] : m(e)]));
108
+ }
109
+ function g(e, t) {
97
110
  let n = {};
98
111
  for (let r of e) t[r.key] !== void 0 && (n[r.key] = t[r.key]);
99
112
  return n;
100
113
  }
101
114
  //#endregion
102
115
  //#region client/lib/playground/panel.css?inline
103
- var g = "@layer settings{:host{--color-surface-primary:var(--styleguide-color-background-highlight,light-dark(#fff,#0a0a0a));--color-surface-sunken:var(--styleguide-color-background,light-dark(#fafafa,#050505));--color-text-primary:var(--styleguide-color-highlight,light-dark(#111827,#fafafa));--color-text-secondary:var(--styleguide-color-regular,light-dark(#666,#a3a3a3));--color-border-default:var(--styleguide-color-border,light-dark(#ebebeb,#1f1f1f));--color-border-focus:var(--styleguide-color-theme-highlight,light-dark(#2463eb,#92c2fc));--color-status-error:light-dark(#ba1c1c,#f87272);--font-mono:ui-monospace, \"Geist Mono\", sfmono-regular, menlo, monospace;--radius-default:.75em;--radius-small:.5em;--duration-fast:.15s;--ease-out:cubic-bezier(.23, 1, .32, 1)}:host([data-standalone]){--color-surface-primary:light-dark(#fff,#0a0a0a);--color-surface-sunken:light-dark(#fafafa,#050505);--color-text-primary:light-dark(#111827,#fafafa);--color-text-secondary:light-dark(#525252,#a3a3a3);--color-border-default:light-dark(#e6e6e6,#262626);--color-border-focus:light-dark(#2463eb,#92c2fc);color-scheme:light dark}:host([data-scheme=light]){color-scheme:light}:host([data-scheme=dark]){color-scheme:dark}}@layer generic{:host{display:block}.c-panel,.c-launcher{color:var(--color-text-secondary);font-size:.875rem;line-height:1.45}:host([data-standalone]) :is(.c-panel,.c-launcher){font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px}*,:before,:after{box-sizing:border-box}:focus-visible{outline:2px solid var(--color-border-focus);outline-offset:2px}[hidden]{display:none!important}}@layer components{.c-panel{border:1px solid var(--color-border-default);border-radius:var(--radius-default);background:var(--color-surface-primary);flex-direction:column;min-block-size:0;max-block-size:100%;display:flex;overflow:hidden}:host([data-standalone]) .c-panel{block-size:100%;box-shadow:0 0 0 1px var(--color-border-default), 0 .5em 2em #0000001f;border-width:0;border-radius:0}:host([data-flush]) .c-panel{border-width:0;border-radius:0}:host([data-flush]) .c-panel__header{display:none}.c-panel__header{border-block-end:1px solid var(--color-border-default);align-items:center;gap:.5em;padding-block:.5em;padding-inline:1em .5em;display:flex}.c-panel__title{min-inline-size:0;color:var(--color-text-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:1em;font-weight:600;line-height:2em;overflow:hidden}.c-panel__body{gap:1em;min-block-size:0;padding:1em;display:grid;overflow-y:auto}.c-panel__message{border-radius:var(--radius-small);color:var(--color-status-error);white-space:pre-wrap;overflow-wrap:anywhere;border:1px solid;margin:0;padding-block:.5em;padding-inline:.75em}.c-panel__footer{border-block-start:1px solid var(--color-border-default);background:var(--color-surface-sunken);justify-content:space-between;align-items:center;gap:.5em;padding-block:.5em;padding-inline:1em;display:flex}.c-panel__call{min-inline-size:0;font-family:var(--font-mono);color:var(--color-text-primary);text-overflow:ellipsis;white-space:nowrap;font-size:.75em;overflow:hidden}.c-field{gap:.25em;display:grid}.c-field__label{font-family:var(--font-mono);color:var(--color-text-primary);font-weight:600}.c-field__type{font-family:var(--font-mono)}.c-field__help,.c-field__error{margin:0;font-size:.75em}.c-field__error{color:var(--color-status-error)}.c-control{border:1px solid var(--color-border-default);border-radius:var(--radius-small);background:var(--color-surface-sunken);inline-size:100%;color:var(--color-text-primary);font:inherit;margin:0;padding-block:.375em;padding-inline:.625em;font-size:max(16px,1em);line-height:1.4;display:block}@media (hover:hover) and (pointer:fine){.c-control{font-size:inherit}}.c-control--select{appearance:none;cursor:pointer;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - .9375em),calc(100% - .625em);background-repeat:no-repeat;background-size:.3125em .3125em;padding-inline-end:2em}.c-control--textarea{resize:vertical;min-block-size:5.5em;font-family:var(--font-mono);font-size:.75em}.c-control[aria-invalid=true]{border-color:var(--color-status-error)}.c-control:disabled,.c-checkbox:disabled{opacity:.5;cursor:not-allowed}.c-checkbox-row{cursor:pointer;align-items:center;gap:.5em;display:inline-flex}.c-checkbox{block-size:1em;inline-size:1em;accent-color:var(--color-border-focus);cursor:pointer;margin:0}.c-button{border:1px solid var(--color-border-default);border-radius:var(--radius-small);background:var(--color-surface-primary);min-block-size:2em;min-inline-size:2em;color:var(--color-text-primary);font:inherit;white-space:nowrap;cursor:pointer;transition-property:border-color,scale;transition-duration:var(--duration-fast);transition-timing-function:var(--ease-out);justify-content:center;align-items:center;gap:.25em;margin:0;padding-block:.25em;padding-inline:.625em;font-size:.875em;display:inline-flex}.c-button:active{scale:.96}@media (hover:hover) and (pointer:fine){.c-button:hover{border-color:var(--color-border-focus)}}.c-button svg{block-size:1em;inline-size:1em}.c-button--icon{min-inline-size:2em;padding-inline:0}.c-button:disabled{opacity:.5;cursor:not-allowed}.c-list-editor{justify-items:start;gap:.5em;display:grid}.c-list{gap:.375em;inline-size:100%;margin:0;padding:0;list-style:none;display:grid}.c-list__item{grid-template-columns:minmax(0,1fr) repeat(3,auto);align-items:center;gap:.25em;display:grid}.c-resize-handle{z-index:1;touch-action:none;background:0 0;border:0;padding:0;position:absolute}.c-resize-handle:after{content:\"\";background:var(--color-text-secondary);opacity:0;transition:opacity var(--duration-fast) var(--ease-out);border-radius:999px;position:absolute}@media (hover:hover) and (pointer:fine){.c-resize-handle:hover:after{opacity:.4}}.c-resize-handle:focus-visible:after{opacity:.6}:host([data-dock=right]) .c-resize-handle{cursor:col-resize;inline-size:.5em;inset-block:0;inset-inline-start:-.25em}:host([data-dock=right]) .c-resize-handle:after{inline-size:2px;inset-block:40%;inset-inline-start:.1875em}:host([data-dock=bottom]) .c-resize-handle{cursor:row-resize;block-size:.5em;inset-block-start:-.25em;inset-inline:0}:host([data-dock=bottom]) .c-resize-handle:after{block-size:2px;inset-block-start:.1875em;inset-inline:40%}.c-launcher{box-shadow:0 .25em 1em #00000029}@media (prefers-reduced-motion:reduce){.c-button,.c-resize-handle:after{transition:none}}}@layer utilities{.u-sr-only{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}";
116
+ var _ = "@layer settings{:host{--color-surface-primary:var(--styleguide-color-background-highlight,light-dark(#fff,#0a0a0a));--color-surface-sunken:var(--styleguide-color-background,light-dark(#fafafa,#050505));--color-text-primary:var(--styleguide-color-highlight,light-dark(#111827,#fafafa));--color-text-secondary:var(--styleguide-color-regular,light-dark(#666,#a3a3a3));--color-border-default:var(--styleguide-color-border,light-dark(#ebebeb,#1f1f1f));--color-border-focus:var(--styleguide-color-theme-highlight,light-dark(#2463eb,#92c2fc));--color-status-error:light-dark(#ba1c1c,#f87272);--font-mono:ui-monospace, \"Geist Mono\", sfmono-regular, menlo, monospace;--radius-default:.75em;--radius-small:.5em;--duration-fast:.15s;--ease-out:cubic-bezier(.23, 1, .32, 1)}:host([data-standalone]){--color-surface-primary:light-dark(#fff,#0a0a0a);--color-surface-sunken:light-dark(#fafafa,#050505);--color-text-primary:light-dark(#111827,#fafafa);--color-text-secondary:light-dark(#525252,#a3a3a3);--color-border-default:light-dark(#e6e6e6,#262626);--color-border-focus:light-dark(#2463eb,#92c2fc);color-scheme:light dark}:host([data-scheme=light]){color-scheme:light}:host([data-scheme=dark]){color-scheme:dark}}@layer generic{:host{display:block}.c-panel,.c-launcher{color:var(--color-text-secondary);font-size:.875rem;line-height:1.45}:host([data-standalone]) :is(.c-panel,.c-launcher){font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px}*,:before,:after{box-sizing:border-box}:focus-visible{outline:2px solid var(--color-border-focus);outline-offset:2px}[hidden]{display:none!important}}@layer components{.c-panel{border:1px solid var(--color-border-default);border-radius:var(--radius-default);background:var(--color-surface-primary);flex-direction:column;min-block-size:0;max-block-size:100%;display:flex;overflow:hidden}:host([data-standalone]) .c-panel{block-size:100%;box-shadow:0 0 0 1px var(--color-border-default), 0 .5em 2em #0000001f;border-width:0;border-radius:0}:host([data-flush]) .c-panel{border-width:0;border-radius:0}:host([data-flush]) .c-panel__header{display:none}.c-panel__header{border-block-end:1px solid var(--color-border-default);align-items:center;gap:.5em;padding-block:.5em;padding-inline:1em .5em;display:flex}.c-panel__title{min-inline-size:0;color:var(--color-text-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:1em;font-weight:600;line-height:2em;overflow:hidden}.c-panel__body{gap:1em;min-block-size:0;padding:1em;display:grid;overflow-y:auto}.c-panel__message{border-radius:var(--radius-small);color:var(--color-status-error);white-space:pre-wrap;overflow-wrap:anywhere;border:1px solid;margin:0;padding-block:.5em;padding-inline:.75em}.c-panel__footer{border-block-start:1px solid var(--color-border-default);background:var(--color-surface-sunken);justify-content:space-between;align-items:center;gap:.5em;padding-block:.5em;padding-inline:1em;display:flex}.c-panel__call{min-inline-size:0;font-family:var(--font-mono);color:var(--color-text-primary);text-overflow:ellipsis;white-space:nowrap;font-size:.75em;overflow:hidden}.c-field{gap:.25em;display:grid}.c-field__label{font-family:var(--font-mono);color:var(--color-text-primary);font-weight:600}.c-field__type{font-family:var(--font-mono)}.c-field__help,.c-field__error{margin:0;font-size:.75em}.c-field__error{color:var(--color-status-error)}.c-control{border:1px solid var(--color-border-default);border-radius:var(--radius-small);background:var(--color-surface-sunken);inline-size:100%;color:var(--color-text-primary);font:inherit;margin:0;padding-block:.375em;padding-inline:.625em;font-size:max(16px,1em);line-height:1.4;display:block}@media (hover:hover) and (pointer:fine){.c-control{font-size:inherit}}.c-control--select{appearance:none;cursor:pointer;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - .9375em),calc(100% - .625em);background-repeat:no-repeat;background-size:.3125em .3125em;padding-inline-end:2em}.c-control--textarea{resize:vertical;min-block-size:5.5em;font-family:var(--font-mono);font-size:.75em}.c-control[aria-invalid=true]{border-color:var(--color-status-error)}.c-control:disabled,.c-checkbox:disabled{opacity:.5;cursor:not-allowed}.c-checkbox-row{cursor:pointer;align-items:center;gap:.5em;display:inline-flex}.c-checkbox{block-size:1em;inline-size:1em;accent-color:var(--color-border-focus);cursor:pointer;margin:0}.c-button{border:1px solid var(--color-border-default);border-radius:var(--radius-small);background:var(--color-surface-primary);min-block-size:2em;min-inline-size:2em;color:var(--color-text-primary);font:inherit;white-space:nowrap;cursor:pointer;transition-property:border-color,scale;transition-duration:var(--duration-fast);transition-timing-function:var(--ease-out);justify-content:center;align-items:center;gap:.25em;margin:0;padding-block:.25em;padding-inline:.625em;font-size:.875em;display:inline-flex}.c-button:active{scale:.96}@media (hover:hover) and (pointer:fine){.c-button:hover{border-color:var(--color-border-focus)}}.c-button svg{block-size:1em;inline-size:1em}.c-button--icon{min-inline-size:2em;padding-inline:0}.c-button:disabled{opacity:.5;cursor:not-allowed}.c-list-editor{justify-items:start;gap:.5em;display:grid}.c-list{gap:.375em;inline-size:100%;margin:0;padding:0;list-style:none;display:grid}.c-list__item{grid-template-columns:minmax(0,1fr) repeat(3,auto);align-items:center;gap:.25em;display:grid}.c-list__item--entry{align-items:start}.c-entry{border:1px solid var(--color-border-default);border-radius:var(--radius-small);gap:.375em;padding:.5em;display:grid}.c-entry__field{gap:.125em;display:grid}.c-entry__label{font-family:var(--font-mono);color:var(--color-text-primary);font-size:.875em}.c-resize-handle{z-index:1;touch-action:none;background:0 0;border:0;padding:0;position:absolute}.c-resize-handle:after{content:\"\";background:var(--color-text-secondary);opacity:0;transition:opacity var(--duration-fast) var(--ease-out);border-radius:999px;position:absolute}@media (hover:hover) and (pointer:fine){.c-resize-handle:hover:after{opacity:.4}}.c-resize-handle:focus-visible:after{opacity:.6}:host([data-dock=right]) .c-resize-handle{cursor:col-resize;inline-size:.5em;inset-block:0;inset-inline-start:-.25em}:host([data-dock=right]) .c-resize-handle:after{inline-size:2px;inset-block:40%;inset-inline-start:.1875em}:host([data-dock=bottom]) .c-resize-handle{cursor:row-resize;block-size:.5em;inset-block-start:-.25em;inset-inline:0}:host([data-dock=bottom]) .c-resize-handle:after{block-size:2px;inset-block-start:.1875em;inset-inline:40%}.c-launcher{box-shadow:0 .25em 1em #00000029}@media (prefers-reduced-motion:reduce){.c-button,.c-resize-handle:after{transition:none}}}@layer utilities{.u-sr-only{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}";
104
117
  //#endregion
105
118
  //#region client/lib/playground/panel.ts
106
- function _(e, t = {}, n = []) {
119
+ function v(e, t = {}, n = []) {
107
120
  let r = document.createElement(e);
108
121
  for (let [e, n] of Object.entries(t)) n === void 0 || n === !1 || r.setAttribute(e, n === !0 ? "" : n);
109
122
  return r.append(...n), r;
110
123
  }
111
- function v(e) {
124
+ function y(e) {
112
125
  return e === void 0 ? void 0 : String(e);
113
126
  }
114
- var y = "—";
115
- function b(e, t, n, r, i, a) {
116
- let o = _("select", {
127
+ var b = "—";
128
+ function x(e, t, n, r, i, a) {
129
+ let o = v("select", {
117
130
  ...r,
118
131
  class: "c-control c-control--select"
119
132
  });
120
- n && o.append(_("option", { value: "" }, [y]));
121
- for (let t of e) o.append(_("option", { value: JSON.stringify(t) }, [String(t)]));
133
+ n && o.append(v("option", { value: "" }, [b]));
134
+ for (let t of e) o.append(v("option", { value: JSON.stringify(t) }, [String(t)]));
122
135
  return o.value = t === void 0 ? n ? "" : JSON.stringify(e[0]) : JSON.stringify(t), o.addEventListener("change", () => i(o.value === "" ? void 0 : JSON.parse(o.value)), { signal: a }), o;
123
136
  }
124
- function x(e, t, n, r, i, a) {
137
+ function S(e, t, n, r, i, a) {
125
138
  switch (e.kind) {
126
139
  case "boolean": {
127
- if (n) return b([!0, !1], t, !0, r, i, a);
128
- let e = _("input", {
140
+ if (n) return x([!0, !1], t, !0, r, i, a);
141
+ let e = v("input", {
129
142
  ...r,
130
143
  class: "c-checkbox",
131
144
  type: "checkbox"
132
145
  });
133
146
  return e.checked = t === !0, e.addEventListener("change", () => i(e.checked), { signal: a }), e;
134
147
  }
135
- case "enum": return b(e.options ?? [], t, n, r, i, a);
148
+ case "enum": return x(e.options ?? [], t, n, r, i, a);
136
149
  case "number":
137
150
  case "integer": {
138
- let n = e.multipleOf ?? (e.kind === "integer" ? 1 : void 0), o = _("input", {
151
+ let n = e.multipleOf ?? (e.kind === "integer" ? 1 : void 0), o = v("input", {
139
152
  ...r,
140
153
  class: "c-control",
141
154
  type: "number",
142
155
  inputmode: e.kind === "integer" ? "numeric" : "decimal",
143
156
  step: n === void 0 ? "any" : String(n),
144
- min: v(e.minimum ?? e.exclusiveMinimum),
145
- max: v(e.maximum ?? e.exclusiveMaximum)
157
+ min: y(e.minimum ?? e.exclusiveMinimum),
158
+ max: y(e.maximum ?? e.exclusiveMaximum)
146
159
  });
147
160
  return o.value = typeof t == "number" ? String(t) : "", o.addEventListener("input", () => i(Number.isNaN(o.valueAsNumber) ? void 0 : o.valueAsNumber), { signal: a }), o;
148
161
  }
149
162
  case "string": {
150
- let o = _("input", {
163
+ let o = v("input", {
151
164
  ...r,
152
165
  class: "c-control",
153
166
  type: "text",
154
167
  inputmode: e.format === "email" ? "email" : e.format === "uri" ? "url" : void 0,
155
168
  autocomplete: "off",
156
169
  spellcheck: "false",
157
- minlength: v(e.minLength),
158
- maxlength: v(e.maxLength)
170
+ minlength: y(e.minLength),
171
+ maxlength: y(e.maxLength)
159
172
  });
160
173
  return o.value = typeof t == "string" ? t : "", o.addEventListener("input", () => i(n && o.value === "" ? void 0 : o.value), { signal: a }), o;
161
174
  }
162
175
  }
163
176
  }
164
- function S(e) {
177
+ function C(e) {
178
+ return e instanceof HTMLInputElement && e.type === "checkbox" ? v("span", { class: "c-checkbox-row" }, [e]) : e;
179
+ }
180
+ function w(e, t, n, r, i, a) {
181
+ let o = typeof t == "object" && t && !Array.isArray(t) ? { ...t } : {}, s = [], c = e.fields.map((e) => {
182
+ let t = `${n}-${e.key}`, r = S(e, o[e.key], e.isOptional, {
183
+ id: t,
184
+ "aria-required": String(!e.isOptional)
185
+ }, (t) => {
186
+ let n = { ...o };
187
+ t === void 0 ? delete n[e.key] : n[e.key] = t, o = n, i(n);
188
+ }, a);
189
+ return s.push(r), v("div", { class: "c-entry__field" }, [v("label", {
190
+ class: "c-entry__label",
191
+ for: t
192
+ }, [e.key]), C(r)]);
193
+ });
194
+ return {
195
+ element: v("div", {
196
+ class: "c-entry",
197
+ role: "group",
198
+ "aria-label": r
199
+ }, c),
200
+ controls: s
201
+ };
202
+ }
203
+ function T(e) {
165
204
  let { host: t, name: n, isStandalone: r = !1 } = e, i = new AbortController(), { signal: a } = i, o = t.shadowRoot ?? t.attachShadow({ mode: "open" });
166
205
  o.replaceChildren(), r && t.setAttribute("data-standalone", "");
167
- let s = _("style", {}, [g]), c = _("div", { class: "c-panel__header" }, [_("h2", { class: "c-panel__title" }, ["Props ", _("span", { class: "c-field__type" }, [`+${n}`])])]), l = _("p", {
206
+ let s = v("style", {}, [_]), c = v("div", { class: "c-panel__header" }, [v("h2", { class: "c-panel__title" }, ["Props ", v("span", { class: "c-field__type" }, [`+${n}`])])]), l = v("p", {
168
207
  class: "c-panel__message",
169
208
  role: "alert",
170
209
  hidden: !0
171
- }), u = _("form", {
210
+ }), u = v("form", {
172
211
  class: "c-panel__body",
173
212
  novalidate: !0,
174
213
  "aria-label": `Props of ${n}`
175
- }), d = _("code", {
214
+ }), d = v("code", {
176
215
  class: "c-panel__call",
177
216
  title: "Pug call"
178
- }), p = _("button", {
217
+ }), f = v("button", {
179
218
  class: "c-button",
180
219
  type: "button"
181
- }, ["Reset"]), m = _("div", { class: "c-panel__footer" }, [d, p]), h = _("section", {
220
+ }, ["Reset"]), m = v("div", { class: "c-panel__footer" }, [d, f]), h = v("section", {
182
221
  class: "c-panel",
183
222
  "aria-label": `Props of ${n}`
184
223
  }, [
@@ -188,93 +227,101 @@ function S(e) {
188
227
  m
189
228
  ]);
190
229
  o.append(s, h);
191
- let v = e.fields, y = structuredClone(e.values), b = /* @__PURE__ */ new Map(), S = () => e.onChange(structuredClone(y)), C = (e, t) => {
192
- y[e] = t, S();
193
- }, w = (e, t) => {
230
+ let g = e.fields, y = structuredClone(e.values), b = /* @__PURE__ */ new Map(), x = () => e.onChange(structuredClone(y)), T = (e, t) => {
231
+ y[e] = t, x();
232
+ }, E = (e, t) => {
194
233
  let n = b.get(e);
195
234
  n && (n.error.textContent = t ?? "", n.error.hidden = !t, n.controls().forEach((e) => e.setAttribute("aria-invalid", String(!!t))));
196
- }, T = (e, t, n) => {
197
- let r = _("ol", { class: "c-list" }), i = _("button", {
235
+ }, D = (e, t, n, r) => {
236
+ let i = v("ol", { class: "c-list" }), o = v("button", {
198
237
  class: "c-button",
199
238
  type: "button"
200
- }, ["Add entry"]), o = _("div", {
239
+ }, ["Add entry"]), s = v("div", {
201
240
  class: "c-list-editor",
202
241
  role: "group",
203
- "aria-labelledby": t,
204
- "aria-describedby": n
205
- }, [r, i]), s = [], c = new AbortController(), l = () => Array.isArray(y[e.key]) ? y[e.key] : [], u = (t) => C(e.key, e.isOptional && t.length === 0 ? void 0 : t), d = (t) => {
206
- let n = (e, t) => {
207
- u(e), d(t);
242
+ "aria-labelledby": n,
243
+ "aria-describedby": r
244
+ }, [i, o]), c = [], l = new AbortController(), u = () => Array.isArray(y[e.key]) ? y[e.key] : [], d = (t) => T(e.key, e.isOptional && t.length === 0 ? void 0 : t), f = (n) => {
245
+ let r = (e, t) => {
246
+ d(e), f(t);
208
247
  };
209
- c.abort(), c = new AbortController();
210
- let a = l();
211
- s = [], r.replaceChildren(...a.map((t, r) => {
212
- let i = x(e.items, t, !1, { "aria-label": `${e.key} entry ${r + 1}` }, (e) => {
213
- let t = [...l()];
214
- t[r] = e, u(t);
215
- }, c.signal);
216
- s.push(i);
217
- let o = (e) => {
218
- let t = [...l()], [i] = t.splice(r, 1);
219
- t.splice(r + e, 0, i), n(t, r + e);
220
- }, d = _("button", {
248
+ l.abort(), l = new AbortController();
249
+ let a = u();
250
+ c = [];
251
+ let s = [];
252
+ i.replaceChildren(...a.map((n, i) => {
253
+ let o = e.items, f = `${e.key} entry ${i + 1}`, p = (e) => {
254
+ let t = [...u()];
255
+ t[i] = e, d(t);
256
+ }, m;
257
+ if (o.kind === "object") {
258
+ let e = w(o, n, `${t}-${i}`, f, p, l.signal);
259
+ m = e.element, c.push(...e.controls), s.push(e.controls[0] ?? m);
260
+ } else {
261
+ let e = S(o, n, !1, { "aria-label": f }, p, l.signal);
262
+ m = C(e), c.push(e), s.push(e);
263
+ }
264
+ let h = (e) => {
265
+ let t = [...u()], [n] = t.splice(i, 1);
266
+ t.splice(i + e, 0, n), r(t, i + e);
267
+ }, g = v("button", {
221
268
  class: "c-button c-button--icon",
222
269
  type: "button",
223
- "aria-label": `Move entry ${r + 1} up`,
224
- disabled: r === 0
225
- }, ["↑"]), f = _("button", {
270
+ "aria-label": `Move entry ${i + 1} up`,
271
+ disabled: i === 0
272
+ }, ["↑"]), _ = v("button", {
226
273
  class: "c-button c-button--icon",
227
274
  type: "button",
228
- "aria-label": `Move entry ${r + 1} down`,
229
- disabled: r === a.length - 1
230
- }, ["↓"]), p = _("button", {
275
+ "aria-label": `Move entry ${i + 1} down`,
276
+ disabled: i === a.length - 1
277
+ }, ["↓"]), y = v("button", {
231
278
  class: "c-button c-button--icon",
232
279
  type: "button",
233
- "aria-label": `Remove entry ${r + 1}`
280
+ "aria-label": `Remove entry ${i + 1}`
234
281
  }, ["✕"]);
235
- return d.addEventListener("click", () => o(-1), { signal: c.signal }), f.addEventListener("click", () => o(1), { signal: c.signal }), p.addEventListener("click", () => {
236
- let e = l().filter((e, t) => t !== r);
237
- n(e, Math.min(r, e.length - 1));
238
- }, { signal: c.signal }), _("li", { class: "c-list__item" }, [
239
- e.items.kind === "boolean" ? _("span", { class: "c-checkbox-row" }, [i]) : i,
240
- d,
241
- f,
242
- p
282
+ return g.addEventListener("click", () => h(-1), { signal: l.signal }), _.addEventListener("click", () => h(1), { signal: l.signal }), y.addEventListener("click", () => {
283
+ let e = u().filter((e, t) => t !== i);
284
+ r(e, Math.min(i, e.length - 1));
285
+ }, { signal: l.signal }), v("li", { class: o.kind === "object" ? "c-list__item c-list__item--entry" : "c-list__item" }, [
286
+ m,
287
+ g,
288
+ _,
289
+ y
243
290
  ]);
244
- })), i.disabled = e.maxItems !== void 0 && a.length >= e.maxItems, t !== void 0 && (s[t] ?? i).focus();
291
+ })), o.disabled = e.maxItems !== void 0 && a.length >= e.maxItems, n !== void 0 && (s[n] ?? o).focus();
245
292
  };
246
- return i.addEventListener("click", () => {
247
- let t = l();
248
- u([...t, f(e.items)]), d(t.length);
249
- }, { signal: a }), d(), {
250
- group: o,
251
- controls: () => [...s, i]
293
+ return o.addEventListener("click", () => {
294
+ let t = u();
295
+ d([...t, p(e.items)]), f(t.length);
296
+ }, { signal: a }), f(), {
297
+ group: s,
298
+ controls: () => [...c, o]
252
299
  };
253
- }, E = (e, t) => {
300
+ }, O = (e, t) => {
254
301
  let n = `field-${t}`, r = `${n}-label`, i = `${n}-help`, o = `${n}-error`, s = [e.description ? i : "", o].filter(Boolean).join(" "), c, l;
255
302
  if (e.kind === "array") {
256
- let t = T(e, r, s);
303
+ let t = D(e, n, r, s);
257
304
  c = t.group, l = t.controls;
258
305
  } else {
259
- let t = x(e, y[e.key], e.isOptional, {
306
+ let t = S(e, y[e.key], e.isOptional, {
260
307
  id: n,
261
308
  "aria-describedby": s,
262
309
  "aria-required": String(!e.isOptional)
263
- }, (t) => C(e.key, t), a);
264
- c = t instanceof HTMLInputElement && t.type === "checkbox" ? _("span", { class: "c-checkbox-row" }, [t]) : t, l = () => [t];
310
+ }, (t) => T(e.key, t), a);
311
+ c = C(t), l = () => [t];
265
312
  }
266
- let u = e.kind === "array" ? _("span", {
313
+ let u = e.kind === "array" ? v("span", {
267
314
  class: "c-field__label",
268
315
  id: r
269
- }, [e.key]) : _("label", {
316
+ }, [e.key]) : v("label", {
270
317
  class: "c-field__label",
271
318
  id: r,
272
319
  for: n
273
- }, [e.key]), d = _("p", {
320
+ }, [e.key]), d = v("p", {
274
321
  class: "c-field__help",
275
322
  id: i,
276
323
  hidden: !e.description
277
- }, [e.description ?? ""]), f = _("p", {
324
+ }, [e.description ?? ""]), f = v("p", {
278
325
  class: "c-field__error",
279
326
  id: o,
280
327
  hidden: !0
@@ -282,7 +329,7 @@ function S(e) {
282
329
  return b.set(e.key, {
283
330
  controls: l,
284
331
  error: f
285
- }), _("div", {
332
+ }), v("div", {
286
333
  class: "c-field",
287
334
  "data-field": e.key
288
335
  }, [
@@ -291,14 +338,14 @@ function S(e) {
291
338
  d,
292
339
  f
293
340
  ]);
294
- }, D = () => {
295
- b = /* @__PURE__ */ new Map(), u.replaceChildren(...v.map(E));
341
+ }, k = () => {
342
+ b = /* @__PURE__ */ new Map(), u.replaceChildren(...g.map(O));
296
343
  };
297
- return D(), u.addEventListener("submit", (e) => e.preventDefault(), { signal: a }), p.addEventListener("click", () => e.onReset(), { signal: a }), {
344
+ return k(), u.addEventListener("submit", (e) => e.preventDefault(), { signal: a }), f.addEventListener("click", () => e.onReset(), { signal: a }), {
298
345
  shadowRoot: o,
299
346
  header: c,
300
347
  setFields: (e, t) => {
301
- v = e, y = structuredClone(t), D();
348
+ g = e, y = structuredClone(t), k();
302
349
  },
303
350
  setIssues: (e) => {
304
351
  let t = /* @__PURE__ */ new Map(), n = [];
@@ -307,10 +354,10 @@ function S(e) {
307
354
  n.push(r.path.length > 0 ? `${r.path.join(".")}: ${r.message}` : r.message);
308
355
  continue;
309
356
  }
310
- let e = r.path.slice(1), i = e.length > 0 ? `Entry ${Number(e[0]) + 1}: ${r.message}` : r.message;
311
- t.set(r.key, [...t.get(r.key) ?? [], i]);
357
+ let [e, i] = r.path.slice(1), a = `${e === void 0 ? "" : `Entry ${Number(e) + 1}: ${i === void 0 ? "" : `${String(i)}: `}`}${r.message}`;
358
+ t.set(r.key, [...t.get(r.key) ?? [], a]);
312
359
  }
313
- for (let e of b.keys()) w(e, t.get(e)?.join("\n"));
360
+ for (let e of b.keys()) E(e, t.get(e)?.join("\n"));
314
361
  l.textContent = n.join("\n"), l.hidden = n.length === 0;
315
362
  },
316
363
  setMessage: (e) => {
@@ -326,7 +373,7 @@ function S(e) {
326
373
  }
327
374
  //#endregion
328
375
  //#region lib/use-id.ts
329
- function C(t) {
376
+ function E(t) {
330
377
  let n = `id-${e(t)}`, r = 0;
331
378
  return (e) => {
332
379
  if (e === void 0) return `${n}-${r++}`;
@@ -336,39 +383,39 @@ function C(t) {
336
383
  }
337
384
  //#endregion
338
385
  //#region client/lib/playground/runtime.ts
339
- async function w(e, t) {
386
+ async function D(e, t) {
340
387
  let n = await fetch(new URL(e, document.baseURI), { cache: t ? "no-store" : "default" });
341
388
  if (!n.ok) throw Error(`Could not load the mixin manifest (${n.status} ${n.statusText})`);
342
389
  return n.json();
343
390
  }
344
- var T = /* @__PURE__ */ new Map();
345
- function E(e) {
391
+ var O = /* @__PURE__ */ new Map();
392
+ function k(e) {
346
393
  if (!e.module) return Promise.reject(/* @__PURE__ */ Error(`Mixin "${e.name}" has no render module`));
347
- let t = new URL(e.module, document.baseURI).href, n = T.get(t);
394
+ let t = new URL(e.module, document.baseURI).href, n = O.get(t);
348
395
  return n || (n = import(
349
396
  /* @vite-ignore */
350
397
  t
351
398
  ).then((t) => {
352
399
  if (typeof t.render != "function") throw TypeError(`${e.module} does not export render()`);
353
400
  return t.render;
354
- }), T.set(t, n)), n;
401
+ }), O.set(t, n)), n;
355
402
  }
356
- function D(e) {
403
+ function A(e) {
357
404
  return typeof e == "object" && !!e && Array.isArray(e.issues);
358
405
  }
359
- function O(e, t, r) {
406
+ function j(e, t, r) {
360
407
  try {
361
408
  return {
362
409
  isValid: !0,
363
410
  html: e({
364
411
  props: r,
365
412
  z: n,
366
- useId: C(`mixin-${t}`),
413
+ useId: E(`mixin-${t}`),
367
414
  modifierClass: ""
368
415
  })
369
416
  };
370
417
  } catch (e) {
371
- return D(e) ? {
418
+ return A(e) ? {
372
419
  isValid: !1,
373
420
  issues: e.issues.map((e) => {
374
421
  let t = e.path.filter((e) => typeof e != "symbol");
@@ -386,8 +433,8 @@ function O(e, t, r) {
386
433
  }
387
434
  //#endregion
388
435
  //#region client/lib/playground/state.ts
389
- var k = "props";
390
- function A(e) {
436
+ var M = "props";
437
+ function N(e) {
391
438
  if (e === null || e === "") return {};
392
439
  try {
393
440
  let t = JSON.parse(e);
@@ -396,64 +443,72 @@ function A(e) {
396
443
  return { error: `\`props\` is not valid JSON: ${e instanceof Error ? e.message : String(e)}` };
397
444
  }
398
445
  }
399
- var j = /^(?:javascript|vbscript|data):/i;
400
- function M(e) {
401
- return j.test([...e].filter((e) => e.charCodeAt(0) > 32).join(""));
446
+ var P = /^(?:javascript|vbscript|data):/i;
447
+ function F(e) {
448
+ return P.test([...e].filter((e) => e.charCodeAt(0) > 32).join(""));
402
449
  }
403
- function N(e) {
404
- if (typeof e == "string") return e.includes("<") || M(e) ? e : void 0;
450
+ function I(e) {
451
+ if (typeof e == "string") return e.includes("<") || F(e) ? e : void 0;
405
452
  if (typeof e == "object" && e) for (let t of Object.values(e)) {
406
- let e = N(t);
453
+ let e = I(t);
407
454
  if (e !== void 0) return e;
408
455
  }
409
456
  }
410
- function P(e) {
411
- let t = A(new URLSearchParams(e.hash.slice(1)).get(k) ?? e.searchParams.get(k)), n = t.props ? N(t.props) : void 0;
457
+ function L(e) {
458
+ let t = N(new URLSearchParams(e.hash.slice(1)).get(M) ?? e.searchParams.get(M)), n = t.props ? I(t.props) : void 0;
412
459
  return n === void 0 ? t : { error: `The props from the link were not applied: they contain markup or a script URL (${JSON.stringify(n.slice(0, 40))}). Enter such values in the controls instead.` };
413
460
  }
414
- function F(e, t) {
461
+ function R(e, t) {
415
462
  let n = new URL(e), r = new URLSearchParams(n.hash.slice(1));
416
- return r.set(k, JSON.stringify(t)), n.hash = r.toString(), n;
463
+ return r.set(M, JSON.stringify(t)), n.hash = r.toString(), n;
417
464
  }
418
- function I(e, t) {
465
+ function z(e, t) {
419
466
  let n = new URL(e);
420
- return n.searchParams.set(k, JSON.stringify(t)), n.hash = "", n;
467
+ return n.searchParams.set(M, JSON.stringify(t)), n.hash = "", n;
421
468
  }
422
- var L = /^[A-Z_$][\w$]*$/i;
423
- function R(e) {
469
+ var B = /^[A-Z_$][\w$]*$/i;
470
+ function V(e) {
424
471
  if (typeof e == "string") return `'${e.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\n", "\\n")}'`;
425
- if (Array.isArray(e)) return `[${e.map(R).join(", ")}]`;
472
+ if (Array.isArray(e)) return `[${e.map(V).join(", ")}]`;
426
473
  if (typeof e == "object" && e) {
427
474
  let t = Object.entries(e);
428
- return t.length === 0 ? "{}" : `{ ${t.map(([e, t]) => `${L.test(e) ? e : R(e)}: ${R(t)}`).join(", ")} }`;
475
+ return t.length === 0 ? "{}" : `{ ${t.map(([e, t]) => `${B.test(e) ? e : V(e)}: ${V(t)}`).join(", ")} }`;
429
476
  }
430
477
  return JSON.stringify(e) ?? "undefined";
431
478
  }
432
- function z(e, t, n = []) {
433
- let r = new Map(n.filter((e) => e.hasDefault).map((e) => [e.key, JSON.stringify(e.default)]));
434
- return `+${e}(${R(Object.fromEntries(Object.entries(t).filter(([e, t]) => r.get(e) !== JSON.stringify(t))))})`;
479
+ function H(e, t) {
480
+ let n = new Map(t.filter((e) => e.hasDefault).map((e) => [e.key, JSON.stringify(e.default)]));
481
+ return Object.fromEntries(Object.entries(e).filter(([e, t]) => n.get(e) !== JSON.stringify(t)));
482
+ }
483
+ function U(e, t, n = []) {
484
+ let r = H(t, n);
485
+ for (let e of n) {
486
+ let { items: t } = e;
487
+ t?.kind === "object" && Array.isArray(r[e.key]) && (r[e.key] = r[e.key].map((e) => typeof e == "object" && e ? H(e, t.fields) : e));
488
+ }
489
+ return `+${e}(${V(r)})`;
435
490
  }
436
491
  //#endregion
437
492
  //#region client/lib/playground/validate.ts
438
- var B = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/;
439
- function V(e) {
493
+ var W = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/;
494
+ function G(e) {
440
495
  return typeof e == "string" ? `"${e}"` : String(e);
441
496
  }
442
- function H(e) {
497
+ function K(e) {
443
498
  try {
444
499
  return !!new URL(e);
445
500
  } catch {
446
501
  return !1;
447
502
  }
448
503
  }
449
- function U(e, t) {
504
+ function q(e, t) {
450
505
  return `${e} ${t}${e === 1 ? "" : "s"}`;
451
506
  }
452
- function W(e, t) {
507
+ function J(e, t) {
453
508
  switch (e.kind) {
454
- case "enum": return e.options?.includes(t) ? void 0 : `Must be one of ${(e.options ?? []).map(V).join(", ")}`;
509
+ case "enum": return e.options?.includes(t) ? void 0 : `Must be one of ${(e.options ?? []).map(G).join(", ")}`;
455
510
  case "boolean": return typeof t == "boolean" ? void 0 : "Must be true or false";
456
- case "string": return typeof t == "string" ? e.minLength !== void 0 && t.length < e.minLength ? e.minLength === 1 ? "Must not be empty" : `Must be at least ${U(e.minLength, "character")}` : e.maxLength !== void 0 && t.length > e.maxLength ? `Must be at most ${U(e.maxLength, "character")}` : e.format === "email" && !B.test(t) ? "Must be an email address" : e.format === "uri" && !H(t) ? "Must be a URL" : e.pattern !== void 0 && e.format !== "email" && !new RegExp(e.pattern).test(t) ? `Must match ${e.pattern}` : void 0 : "Must be text";
511
+ case "string": return typeof t == "string" ? e.minLength !== void 0 && t.length < e.minLength ? e.minLength === 1 ? "Must not be empty" : `Must be at least ${q(e.minLength, "character")}` : e.maxLength !== void 0 && t.length > e.maxLength ? `Must be at most ${q(e.maxLength, "character")}` : e.format === "email" && !W.test(t) ? "Must be an email address" : e.format === "uri" && !K(t) ? "Must be a URL" : e.pattern !== void 0 && e.format !== "email" && !new RegExp(e.pattern).test(t) ? `Must match ${e.pattern}` : void 0 : "Must be text";
457
512
  case "number":
458
513
  case "integer":
459
514
  if (typeof t != "number" || Number.isNaN(t)) return "Must be a number";
@@ -469,40 +524,46 @@ function W(e, t) {
469
524
  return;
470
525
  }
471
526
  }
472
- function G(e, t) {
527
+ function Y(e, t) {
528
+ return typeof t != "object" || !t || Array.isArray(t) ? ["Must be an object"] : e.fields.flatMap((e) => X(e, t[e.key]).map((t) => `${e.key}: ${t}`));
529
+ }
530
+ function X(e, t) {
473
531
  if (t === void 0) return e.isOptional ? [] : ["Required"];
474
532
  if (e.kind !== "array") {
475
- let n = W(e, t);
533
+ let n = J(e, t);
476
534
  return n ? [n] : [];
477
535
  }
478
536
  if (!Array.isArray(t)) return ["Must be a list"];
479
537
  let n = [];
480
- return e.minItems !== void 0 && t.length < e.minItems && n.push(`Needs at least ${U(e.minItems, "entry").replace("entrys", "entries")}`), e.maxItems !== void 0 && t.length > e.maxItems && n.push(`Allows at most ${U(e.maxItems, "entry").replace("entrys", "entries")}`), t.forEach((t, r) => {
481
- let i = e.items ? W(e.items, t) : void 0;
482
- i && n.push(`Entry ${r + 1}: ${i}`);
538
+ e.minItems !== void 0 && t.length < e.minItems && n.push(`Needs at least ${q(e.minItems, "entry").replace("entrys", "entries")}`), e.maxItems !== void 0 && t.length > e.maxItems && n.push(`Allows at most ${q(e.maxItems, "entry").replace("entrys", "entries")}`);
539
+ let { items: r } = e;
540
+ return t.forEach((e, t) => {
541
+ if (!r) return;
542
+ let i = r.kind === "object" ? Y(r, e) : [J(r, e)].filter((e) => e !== void 0);
543
+ n.push(...i.map((e) => `Entry ${t + 1}: ${e}`));
483
544
  }), n;
484
545
  }
485
- function K(e, t) {
546
+ function Z(e, t) {
486
547
  let n = /* @__PURE__ */ new Map();
487
548
  for (let r of e) {
488
- let e = G(r, t[r.key]);
549
+ let e = X(r, t[r.key]);
489
550
  e.length > 0 && n.set(r.key, e);
490
551
  }
491
552
  return n;
492
553
  }
493
554
  //#endregion
494
555
  //#region client/lib/playground/controller.ts
495
- var q = 1e3;
496
- async function J(e, n, r = {}) {
497
- let i = (await w(e.manifestUrl, e.isDevelopment)).mixins.find((t) => t.id === e.id);
556
+ var Q = 1e3;
557
+ async function $(e, n, r = {}) {
558
+ let i = (await D(e.manifestUrl, e.isDevelopment)).mixins.find((t) => t.id === e.id);
498
559
  if (!i || !i.hasSchema) throw Error(`Mixin "${e.name}" has no playground in ${e.manifestUrl}`);
499
- let a = d(i.jsonSchema), o = m(a, r.props), s = i.module ? await E(i) : void 0, c, l = () => h(a, o), u = () => z(i.name, l(), a), f = () => {
560
+ let a = d(i.jsonSchema), o = h(a, r.props), s = i.module ? await k(i) : void 0, c, l = () => g(a, o), u = () => U(i.name, l(), a), f = () => {
500
561
  let e = l();
501
562
  if (c?.setPugCall(u()), !s) {
502
563
  c?.setMessage(i.error?.message ?? "The mixin has not compiled yet.");
503
564
  return;
504
565
  }
505
- let t = K(a, o);
566
+ let t = Z(a, o);
506
567
  if (t.size > 0) {
507
568
  let r = [...t].flatMap(([e, t]) => t.map((t) => ({
508
569
  key: e,
@@ -515,14 +576,14 @@ async function J(e, n, r = {}) {
515
576
  }, e);
516
577
  return;
517
578
  }
518
- let r = O(s, i.id, e);
579
+ let r = j(s, i.id, e);
519
580
  if (r.isValid) {
520
581
  c?.setIssues([]), n.onRender(r.html, e);
521
582
  return;
522
583
  }
523
584
  "issues" in r ? c?.setIssues(r.issues) : (c?.setIssues([]), c?.setMessage(r.error)), n.onInvalid?.(r, e);
524
585
  };
525
- if (n.panelHost && (c = S({
586
+ if (n.panelHost && (c = T({
526
587
  host: n.panelHost,
527
588
  name: i.name,
528
589
  fields: a,
@@ -532,7 +593,7 @@ async function J(e, n, r = {}) {
532
593
  o = e, n.onPropsChange?.(l()), f();
533
594
  },
534
595
  onReset: () => {
535
- o = m(a), c.setFields(a, o), n.onPropsChange?.(l()), f();
596
+ o = h(a), c.setFields(a, o), n.onPropsChange?.(l()), f();
536
597
  }
537
598
  }), n.onPanel?.(c)), n.onCompileError?.(i.error), f(), r.error) {
538
599
  let e = {
@@ -545,33 +606,33 @@ async function J(e, n, r = {}) {
545
606
  issues: [e]
546
607
  }, l());
547
608
  }
548
- let p, g = new AbortController();
609
+ let p, m = new AbortController();
549
610
  if (e.isDevelopment) {
550
611
  let r = !1;
551
612
  p = new t(async () => {
552
613
  if (!r) {
553
614
  r = !0;
554
615
  try {
555
- let t = (await w(e.manifestUrl, !0)).mixins.find((t) => t.id === e.id);
616
+ let t = (await D(e.manifestUrl, !0)).mixins.find((t) => t.id === e.id);
556
617
  if (!t || !t.hasSchema) return;
557
618
  let r = i;
558
619
  i = t;
559
620
  let u = JSON.stringify(t.jsonSchema) !== JSON.stringify(r.jsonSchema);
560
621
  if (u) {
561
622
  a = d(t.jsonSchema);
562
- let e = l(), n = m(a);
623
+ let e = l(), n = h(a);
563
624
  o = Object.fromEntries(a.map((t) => [t.key, t.key in e ? e[t.key] : n[t.key]])), c?.setFields(a, o);
564
625
  }
565
- JSON.stringify(t.error) !== JSON.stringify(r.error) && n.onCompileError?.(t.error), t.module !== r.module && t.module && (s = await E(t)), (t.module !== r.module || u) && f();
626
+ JSON.stringify(t.error) !== JSON.stringify(r.error) && n.onCompileError?.(t.error), t.module !== r.module && t.module && (s = await k(t)), (t.module !== r.module || u) && f();
566
627
  } catch (e) {
567
628
  console.warn("[kss-modern] Could not check the mixin manifest for updates", e);
568
629
  } finally {
569
630
  r = !1;
570
631
  }
571
632
  }
572
- }, q), document.visibilityState === "visible" && p.start(), document.addEventListener("visibilitychange", () => {
633
+ }, Q), document.visibilityState === "visible" && p.start(), document.addEventListener("visibilitychange", () => {
573
634
  document.visibilityState === "visible" ? p.resume() : p.pause();
574
- }, { signal: g.signal });
635
+ }, { signal: m.signal });
575
636
  }
576
637
  return {
577
638
  get entry() {
@@ -584,9 +645,9 @@ async function J(e, n, r = {}) {
584
645
  getPugCall: u,
585
646
  render: f,
586
647
  destroy: () => {
587
- p?.stop(), g.abort(), c?.destroy();
648
+ p?.stop(), m.abort(), c?.destroy();
588
649
  }
589
650
  };
590
651
  }
591
652
  //#endregion
592
- export { w as a, o as c, I as i, i as l, P as n, _ as o, F as r, a as s, J as t, r as u };
653
+ export { D as a, o as c, z as i, i as l, L as n, v as o, R as r, a as s, $ as t, r as u };
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./code-highlight-Fm7cxf-E.js";
2
2
  import { n as t } from "./utils-DtB-AzJa.js";
3
- import { a as n, i as r, n as i, r as a, s as o, t as s, u as c } from "./controller-BLn-0hDZ.js";
3
+ import { a as n, i as r, n as i, r as a, s as o, t as s, u as c } from "./controller-BduQiyRp.js";
4
4
  import { t as l } from "./alerts-DIQ8TeZB.js";
5
5
  //#region client/lib/playground/format-html.ts
6
6
  var u = /* @__PURE__ */ new Set([
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./shared-hGcWlnRM.js";
2
- import { c as t, i as n, l as r, n as i, o as a, r as o, t as s } from "./controller-BLn-0hDZ.js";
2
+ import { c as t, i as n, l as r, n as i, o as a, r as o, t as s } from "./controller-BduQiyRp.js";
3
3
  //#region client/playground.ts
4
4
  var c = "kss-modern:playground-panel", l = {
5
5
  dock: "right",
@@ -1139,7 +1139,7 @@ ot && import("./global-theme-select-CdFgPJ0T.js").then(({ default: e }) => e(ot)
1139
1139
  var st = document.querySelectorAll("[data-section-theme-select]");
1140
1140
  st.length > 0 && import("./section-theme-select-CO65kL5N.js").then(({ default: e }) => e(st)).catch(console.error);
1141
1141
  var ct = document.querySelector("[data-mixin-explorer]");
1142
- ct && import("./explorer-ADHDxFj8.js").then(({ default: e }) => e(ct)).catch(console.error);
1142
+ ct && import("./explorer-B2UH96jd.js").then(({ default: e }) => e(ct)).catch(console.error);
1143
1143
  var lt = document.querySelector(".editor-select");
1144
1144
  lt && import("./editor-select-DIZFtqe7.js").then(({ default: e }) => e(lt)).catch(console.error);
1145
1145
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kss-modern",
3
3
  "type": "module",
4
- "version": "1.7.0-beta.0",
4
+ "version": "1.7.0-beta.2",
5
5
  "description": "Modern KSS compatible styleguide generator",
6
6
  "keywords": [
7
7
  "kss",