@getvitops/emdash 1.0.0-alpha.0 → 1.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/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { designSystemPath, formatConfig, getAt, isConfig, mergePatch, resolveInp
4
4
  import { buildReverseIndex, patchToOverrideCss, validateColorContract } from "@getvitops/generator/tokens";
5
5
  import { commitFile } from "@getvitops/utils/git";
6
6
 
7
- //#region src/icon-options.ts
7
+ // src/icon-options.ts
8
8
  /**
9
9
  * Semantic icon names, as Block Kit combobox options.
10
10
  *
@@ -25,8 +25,7 @@ const SEMANTIC_ICON_OPTIONS = Object.keys(iconMap.fa7).sort().map((name) => ({
25
25
  value: name
26
26
  }));
27
27
 
28
- //#endregion
29
- //#region src/blocks.ts
28
+ // src/blocks.ts
30
29
  const BLOCKS = [
31
30
  {
32
31
  /**
@@ -270,17 +269,34 @@ const BLOCKS = [
270
269
  type: "vitops.carousel",
271
270
  label: "Carousel",
272
271
  placeholder: "Image carousel",
272
+ category: "Media",
273
273
  fields: [
274
+ {
275
+ type: "repeater",
276
+ action_id: "slides",
277
+ label: "Slides",
278
+ item_label: "slide",
279
+ min_items: 1,
280
+ fields: [{
281
+ type: "text_input",
282
+ action_id: "src",
283
+ label: "Image URL"
284
+ }, {
285
+ type: "text_input",
286
+ action_id: "alt",
287
+ label: "Alt text"
288
+ }]
289
+ },
274
290
  {
275
291
  type: "text_input",
276
292
  action_id: "images",
277
- label: "Image URLs (one per line)",
293
+ label: "Image URLs, one per line (legacy — use Slides)",
278
294
  multiline: true
279
295
  },
280
296
  {
281
297
  type: "text_input",
282
298
  action_id: "alts",
283
- label: "Alt texts (one per line, same order)",
299
+ label: "Alt texts, one per line (legacy — use Slides)",
284
300
  multiline: true
285
301
  },
286
302
  {
@@ -308,11 +324,302 @@ const BLOCKS = [
308
324
  initial_value: "16 / 9"
309
325
  }
310
326
  ]
327
+ },
328
+ {
329
+ type: "vitops.gallery",
330
+ label: "Gallery",
331
+ placeholder: "Thumbnail grid that opens a lightbox carousel",
332
+ category: "Media",
333
+ fields: [
334
+ {
335
+ type: "text_input",
336
+ action_id: "label",
337
+ label: "Accessible name",
338
+ initial_value: "Gallery"
339
+ },
340
+ {
341
+ type: "repeater",
342
+ action_id: "images",
343
+ label: "Images",
344
+ item_label: "image",
345
+ min_items: 1,
346
+ fields: [
347
+ {
348
+ type: "text_input",
349
+ action_id: "src",
350
+ label: "Image URL"
351
+ },
352
+ {
353
+ type: "text_input",
354
+ action_id: "alt",
355
+ label: "Alt text"
356
+ },
357
+ {
358
+ type: "text_input",
359
+ action_id: "caption",
360
+ label: "Caption"
361
+ }
362
+ ]
363
+ },
364
+ {
365
+ type: "text_input",
366
+ action_id: "aspect",
367
+ label: "Thumbnail aspect ratio",
368
+ initial_value: "1"
369
+ },
370
+ {
371
+ type: "toggle",
372
+ action_id: "loop",
373
+ label: "Wrap around at the ends",
374
+ initial_value: false
375
+ }
376
+ ]
377
+ },
378
+ {
379
+ type: "vitops.cards",
380
+ label: "Cards",
381
+ placeholder: "A grid of cards",
382
+ category: "Sections",
383
+ fields: [{
384
+ type: "repeater",
385
+ action_id: "cards",
386
+ label: "Cards",
387
+ item_label: "card",
388
+ min_items: 1,
389
+ fields: [
390
+ {
391
+ type: "text_input",
392
+ action_id: "title",
393
+ label: "Title"
394
+ },
395
+ {
396
+ type: "text_input",
397
+ action_id: "body",
398
+ label: "Text",
399
+ multiline: true
400
+ },
401
+ {
402
+ type: "text_input",
403
+ action_id: "image",
404
+ label: "Image URL"
405
+ },
406
+ {
407
+ type: "text_input",
408
+ action_id: "imageAlt",
409
+ label: "Image alt text"
410
+ },
411
+ {
412
+ type: "text_input",
413
+ action_id: "href",
414
+ label: "Link URL (makes the whole card clickable)"
415
+ },
416
+ {
417
+ type: "text_input",
418
+ action_id: "linkLabel",
419
+ label: "Link text",
420
+ initial_value: "Read more"
421
+ }
422
+ ]
423
+ }, {
424
+ type: "select",
425
+ action_id: "columns",
426
+ label: "Columns",
427
+ initial_value: "3",
428
+ options: [
429
+ {
430
+ label: "1",
431
+ value: "1"
432
+ },
433
+ {
434
+ label: "2",
435
+ value: "2"
436
+ },
437
+ {
438
+ label: "3",
439
+ value: "3"
440
+ },
441
+ {
442
+ label: "4",
443
+ value: "4"
444
+ }
445
+ ]
446
+ }]
447
+ },
448
+ {
449
+ type: "vitops.accordion",
450
+ label: "Accordion",
451
+ placeholder: "A group of collapsible sections",
452
+ category: "Sections",
453
+ fields: [{
454
+ type: "repeater",
455
+ action_id: "items",
456
+ label: "Items",
457
+ item_label: "item",
458
+ min_items: 1,
459
+ fields: [
460
+ {
461
+ type: "text_input",
462
+ action_id: "summary",
463
+ label: "Summary (always visible)"
464
+ },
465
+ {
466
+ type: "text_input",
467
+ action_id: "body",
468
+ label: "Body (revealed)",
469
+ multiline: true
470
+ },
471
+ {
472
+ type: "toggle",
473
+ action_id: "open",
474
+ label: "Open by default",
475
+ initial_value: false
476
+ }
477
+ ]
478
+ }, {
479
+ type: "toggle",
480
+ action_id: "exclusive",
481
+ label: "Only one open at a time",
482
+ initial_value: false
483
+ }]
484
+ },
485
+ {
486
+ type: "vitops.hero",
487
+ label: "Hero",
488
+ placeholder: "Page opener: heading, text, buttons and an optional image",
489
+ category: "Sections",
490
+ fields: [
491
+ {
492
+ type: "text_input",
493
+ action_id: "eyebrow",
494
+ label: "Eyebrow (small line above)"
495
+ },
496
+ {
497
+ type: "text_input",
498
+ action_id: "heading",
499
+ label: "Heading"
500
+ },
501
+ {
502
+ type: "text_input",
503
+ action_id: "body",
504
+ label: "Text",
505
+ multiline: true
506
+ },
507
+ {
508
+ type: "text_input",
509
+ action_id: "primaryLabel",
510
+ label: "Main button text"
511
+ },
512
+ {
513
+ type: "text_input",
514
+ action_id: "primaryHref",
515
+ label: "Main button URL"
516
+ },
517
+ {
518
+ type: "text_input",
519
+ action_id: "secondaryLabel",
520
+ label: "Second link text"
521
+ },
522
+ {
523
+ type: "text_input",
524
+ action_id: "secondaryHref",
525
+ label: "Second link URL"
526
+ },
527
+ {
528
+ type: "text_input",
529
+ action_id: "image",
530
+ label: "Image URL (split layout)"
531
+ },
532
+ {
533
+ type: "text_input",
534
+ action_id: "imageAlt",
535
+ label: "Image alt text"
536
+ },
537
+ {
538
+ type: "select",
539
+ action_id: "layout",
540
+ label: "Layout",
541
+ initial_value: "centered",
542
+ options: [{
543
+ label: "Centered",
544
+ value: "centered"
545
+ }, {
546
+ label: "Text and image side by side",
547
+ value: "split"
548
+ }]
549
+ }
550
+ ]
551
+ },
552
+ {
553
+ type: "vitops.marquee",
554
+ label: "Marquee",
555
+ placeholder: "A continuously scrolling row (logos, quotes, badges)",
556
+ category: "Media",
557
+ fields: [
558
+ {
559
+ type: "repeater",
560
+ action_id: "items",
561
+ label: "Items",
562
+ item_label: "item",
563
+ min_items: 1,
564
+ fields: [
565
+ {
566
+ type: "text_input",
567
+ action_id: "text",
568
+ label: "Text"
569
+ },
570
+ {
571
+ type: "text_input",
572
+ action_id: "image",
573
+ label: "Image URL (shown instead of text)"
574
+ },
575
+ {
576
+ type: "text_input",
577
+ action_id: "alt",
578
+ label: "Image alt text"
579
+ }
580
+ ]
581
+ },
582
+ {
583
+ type: "toggle",
584
+ action_id: "pauseOnHover",
585
+ label: "Pause on hover",
586
+ initial_value: true
587
+ },
588
+ {
589
+ type: "toggle",
590
+ action_id: "reverse",
591
+ label: "Scroll the other way",
592
+ initial_value: false
593
+ }
594
+ ]
311
595
  }
312
596
  ];
597
+ /**
598
+ * Resolve {@link BlockSelection} against {@link BLOCKS}.
599
+ *
600
+ * Pure so a misconfiguration fails at plugin construction, with the offending name in the
601
+ * message, rather than as an editor menu that is quietly missing something.
602
+ */
603
+ function selectBlocks(selection = {}) {
604
+ const known = new Set(BLOCKS.map((b) => b.type));
605
+ const check = (names, option) => {
606
+ for (const name of names ?? []) if (!known.has(name)) throw new Error(`[vitops] blocks.${option}: unknown block '${name}'. Known: ${[...known].join(", ")}.`);
607
+ };
608
+ check(selection.include, "include");
609
+ check(selection.exclude, "exclude");
610
+ const include = selection.include ? new Set(selection.include) : null;
611
+ const exclude = new Set(selection.exclude);
612
+ const base = BLOCKS.filter((b) => (!include || include.has(b.type)) && !exclude.has(b.type));
613
+ const extra = selection.extra ?? [];
614
+ const taken = new Set(BLOCKS.map((b) => b.type));
615
+ for (const block of extra) {
616
+ if (taken.has(block.type)) throw new Error(`[vitops] blocks.extra: type '${block.type}' is already defined.`);
617
+ taken.add(block.type);
618
+ }
619
+ return [...base, ...extra];
620
+ }
313
621
 
314
- //#endregion
315
- //#region src/design-system-route.ts
622
+ // src/design-system-route.ts
316
623
  /**
317
624
  * The `vitops.design-system` plugin route — what `<wc-theme-editor>` probes
318
625
  * and POSTs its patch to when mounted inside the EmDash admin (see this
@@ -453,8 +760,53 @@ async function designSystemOverrideFragment(ctx, opts) {
453
760
  };
454
761
  }
455
762
 
456
- //#endregion
457
- //#region src/hosting.ts
763
+ // src/blocks-doc.ts
764
+ const cell = (s) => (s ?? "").replace(/\|/g, "\\|").replace(/\n/g, " ");
765
+ function describe(f) {
766
+ const bits = [];
767
+ if ("options" in f && Array.isArray(f.options) && f.options.length) {
768
+ const values = f.options.map((o) => String(o.value));
769
+ bits.push(values.length > 8 ? `one of ${values.length} values (e.g. ${values.slice(0, 4).join(", ")})` : `one of ${values.join(", ")}`);
770
+ }
771
+ if ("initial_value" in f && f.initial_value !== void 0) bits.push(`default \`${String(f.initial_value)}\``);
772
+ if (f.type === "repeater" && "min_items" in f && f.min_items) bits.push(`min ${f.min_items} rows`);
773
+ return bits.join("; ");
774
+ }
775
+ function fieldRows(fields, prefix = "") {
776
+ return fields.flatMap((f) => {
777
+ const own = `| \`${prefix}${f.action_id}\` | ${f.type} | ${cell(f.label)} | ${cell(describe(f))} |`;
778
+ return f.type === "repeater" ? [own, ...fieldRows(f.fields, `${f.action_id}[].`)] : [own];
779
+ });
780
+ }
781
+ /**
782
+ * The block reference an agent reads via `vitops docs blocks`: every Portable Text `_type`,
783
+ * its fields and what each saves. Rendered from the same `BLOCKS` the editor menu uses, so
784
+ * it cannot describe a field the editor doesn't offer.
785
+ */
786
+ function renderBlocksDoc(blocks = BLOCKS) {
787
+ const out = [
788
+ "---",
789
+ "type: concept",
790
+ "title: EmDash blocks",
791
+ "description: Portable Text block types from @getvitops/emdash, with every field",
792
+ "---",
793
+ "",
794
+ "# EmDash blocks",
795
+ "",
796
+ "Each block is a Portable Text object: `{ \"_type\": \"vitops.cards\", \"_key\": \"<unique>\", ...fields }`.",
797
+ "Blocks are flat — a block cannot contain other blocks, and a repeater row holds scalar fields only.",
798
+ "Give every block a unique `_key`; gallery and carousel derive their element id from it.",
799
+ ""
800
+ ];
801
+ for (const b of blocks) {
802
+ out.push(`## \`${b.type}\` — ${b.label}`, "");
803
+ if (b.placeholder) out.push(`${b.placeholder}.`, "");
804
+ if (b.fields?.length) out.push("| Field | Type | Label | Notes |", "| --- | --- | --- | --- |", ...fieldRows(b.fields), "");
805
+ }
806
+ return out.join("\n");
807
+ }
808
+
809
+ // src/hosting.ts
458
810
  const TARGETS = ["cloudflare", "node"];
459
811
  /**
460
812
  * Import an optional hosting dependency. The specifier is passed as a
@@ -483,7 +835,8 @@ async function vitopsHosting(options = {}) {
483
835
  adapter: cloudflare(),
484
836
  database: d1({
485
837
  binding: cf.dbBinding ?? "DB",
486
- session: cf.session ?? "auto"
838
+ session: cf.session ?? "auto",
839
+ coalesce: cf.coalesce ?? true
487
840
  }),
488
841
  storage: r2({ binding: cf.mediaBinding ?? "MEDIA" })
489
842
  };
@@ -513,12 +866,10 @@ async function vitopsHosting(options = {}) {
513
866
  throw new Error(`[vitops] unknown hosting target '${target}' (from ${process.env.HOSTING ? "HOSTING env var" : "options.target"}). Valid targets: ${TARGETS.join(", ")}.`);
514
867
  }
515
868
 
516
- //#endregion
517
- //#region package.json
518
- var version = "1.0.0-alpha.0";
869
+ // package.json
870
+ var version = "1.0.0";
519
871
 
520
- //#endregion
521
- //#region src/index.ts
872
+ // src/index.ts
522
873
  /**
523
874
  * @getvitops/emdash — EmDash CMS native plugin for the Vitops design system.
524
875
  *
@@ -605,7 +956,7 @@ function createPlugin(options = {}) {
605
956
  ...editor ? { allowedHosts: ["api.github.com"] } : {},
606
957
  ...fragmentsEnabled ? { hooks: { "page:fragments": pageFragments } } : {},
607
958
  admin: {
608
- portableTextBlocks: BLOCKS,
959
+ portableTextBlocks: selectBlocks(options.blocks),
609
960
  ...editor ? {
610
961
  entry: "@getvitops/emdash/admin",
611
962
  pages: [{
@@ -619,6 +970,5 @@ function createPlugin(options = {}) {
619
970
  });
620
971
  }
621
972
 
622
- //#endregion
623
- export { BLOCKS, createPlugin, createPlugin as default, vitopsEmdash, vitopsHosting };
973
+ export { BLOCKS, createPlugin, createPlugin as default, renderBlocksDoc, selectBlocks, vitopsEmdash, vitopsHosting };
624
974
  //# sourceMappingURL=index.mjs.map