dreamcontext 0.5.4 → 0.6.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.
Files changed (107) hide show
  1. package/LICENSE +201 -21
  2. package/NOTICE +7 -0
  3. package/README.md +77 -12
  4. package/agents/sleep-product.md +29 -8
  5. package/agents/sleep-tasks.md +44 -9
  6. package/dist/agents/sleep-product.md +29 -8
  7. package/dist/agents/sleep-tasks.md +44 -9
  8. package/dist/dashboard/assets/{BrainCanvas3D-1HMiA14F.js → BrainCanvas3D-LLqVeXtb.js} +1 -1
  9. package/dist/dashboard/assets/{_baseUniq-C7pfRmXz.js → _baseUniq-DW0uA0ty.js} +1 -1
  10. package/dist/dashboard/assets/{arc-B5x_GF9Q.js → arc-fVUGtlbL.js} +1 -1
  11. package/dist/dashboard/assets/{architectureDiagram-Q4EWVU46-CWmjaqno.js → architectureDiagram-Q4EWVU46-Dz2PKBsS.js} +1 -1
  12. package/dist/dashboard/assets/{blockDiagram-DXYQGD6D-B7SyUIdV.js → blockDiagram-DXYQGD6D-ByWKxzhL.js} +1 -1
  13. package/dist/dashboard/assets/{c4Diagram-AHTNJAMY-OeJBWXHU.js → c4Diagram-AHTNJAMY-dpHsVM3D.js} +1 -1
  14. package/dist/dashboard/assets/channel-zrLwggBX.js +1 -0
  15. package/dist/dashboard/assets/{chunk-4BX2VUAB-2Oop1B0M.js → chunk-4BX2VUAB-HEXb6Yg5.js} +1 -1
  16. package/dist/dashboard/assets/{chunk-4TB4RGXK-Bh7qSu_X.js → chunk-4TB4RGXK-DeVy5g6H.js} +1 -1
  17. package/dist/dashboard/assets/{chunk-55IACEB6-gNeHRO_i.js → chunk-55IACEB6-DGy3ZgDZ.js} +1 -1
  18. package/dist/dashboard/assets/{chunk-EDXVE4YY-BRniAH53.js → chunk-EDXVE4YY-CYJehjz4.js} +1 -1
  19. package/dist/dashboard/assets/{chunk-FMBD7UC4-B5r8U2gJ.js → chunk-FMBD7UC4-DKTOmvgZ.js} +1 -1
  20. package/dist/dashboard/assets/{chunk-OYMX7WX6-Dd1c17fm.js → chunk-OYMX7WX6-mmtWyiDA.js} +1 -1
  21. package/dist/dashboard/assets/{chunk-QZHKN3VN-BIw-OdTn.js → chunk-QZHKN3VN-DSdV0iwY.js} +1 -1
  22. package/dist/dashboard/assets/{chunk-YZCP3GAM-Dl9MlwCW.js → chunk-YZCP3GAM-DbY-KoDn.js} +1 -1
  23. package/dist/dashboard/assets/classDiagram-6PBFFD2Q-BvejNiwH.js +1 -0
  24. package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-BvejNiwH.js +1 -0
  25. package/dist/dashboard/assets/clone-BFVdml6g.js +1 -0
  26. package/dist/dashboard/assets/{cose-bilkent-S5V4N54A-DlrROH5K.js → cose-bilkent-S5V4N54A-p0t1DY88.js} +1 -1
  27. package/dist/dashboard/assets/{dagre-KV5264BT-B0GpPXKL.js → dagre-KV5264BT-DYE0WzHM.js} +1 -1
  28. package/dist/dashboard/assets/{diagram-5BDNPKRD-BBYcDW2Z.js → diagram-5BDNPKRD-D9EiQCOP.js} +1 -1
  29. package/dist/dashboard/assets/{diagram-G4DWMVQ6-BxHxugdp.js → diagram-G4DWMVQ6-poDXSAfu.js} +1 -1
  30. package/dist/dashboard/assets/{diagram-MMDJMWI5-C9Jc19Wo.js → diagram-MMDJMWI5-CHZ7wgN1.js} +1 -1
  31. package/dist/dashboard/assets/{diagram-TYMM5635-Vmo5GAAU.js → diagram-TYMM5635-DVk6fYG7.js} +1 -1
  32. package/dist/dashboard/assets/{erDiagram-SMLLAGMA-pskr9x-V.js → erDiagram-SMLLAGMA-D2sqkGin.js} +1 -1
  33. package/dist/dashboard/assets/{flowDiagram-DWJPFMVM-B1D7Ky0W.js → flowDiagram-DWJPFMVM-DrMlKBYA.js} +1 -1
  34. package/dist/dashboard/assets/{ganttDiagram-T4ZO3ILL-BVJZZV1M.js → ganttDiagram-T4ZO3ILL-Cf4FbEFp.js} +1 -1
  35. package/dist/dashboard/assets/{gitGraphDiagram-UUTBAWPF-BiOGN6We.js → gitGraphDiagram-UUTBAWPF-BsbOq1C9.js} +1 -1
  36. package/dist/dashboard/assets/{graph-D58deEXr.js → graph-CUxxgCtS.js} +1 -1
  37. package/dist/dashboard/assets/index-CqSkXBSu.css +1 -0
  38. package/dist/dashboard/assets/index-flRpQtDj.js +476 -0
  39. package/dist/dashboard/assets/{infoDiagram-42DDH7IO-DgJYg1X1.js → infoDiagram-42DDH7IO-DTkMnZiD.js} +1 -1
  40. package/dist/dashboard/assets/{ishikawaDiagram-UXIWVN3A-arJgr-8P.js → ishikawaDiagram-UXIWVN3A-CahQ348K.js} +1 -1
  41. package/dist/dashboard/assets/{journeyDiagram-VCZTEJTY-B3DCvb8p.js → journeyDiagram-VCZTEJTY-BDY2Nslc.js} +1 -1
  42. package/dist/dashboard/assets/{kanban-definition-6JOO6SKY-B--dN6Ma.js → kanban-definition-6JOO6SKY-CgrwoTjI.js} +1 -1
  43. package/dist/dashboard/assets/{layout-z2qEbZWC.js → layout-IaUxkFkm.js} +1 -1
  44. package/dist/dashboard/assets/{linear-BQu1dwrM.js → linear-shc0iNFn.js} +1 -1
  45. package/dist/dashboard/assets/{min-DkF5b2sh.js → min-BIL7YgTN.js} +1 -1
  46. package/dist/dashboard/assets/{mindmap-definition-QFDTVHPH-Bw04koyq.js → mindmap-definition-QFDTVHPH-GmO0JRtA.js} +1 -1
  47. package/dist/dashboard/assets/{pieDiagram-DEJITSTG-vNqz64o8.js → pieDiagram-DEJITSTG-Bfm_toYA.js} +1 -1
  48. package/dist/dashboard/assets/{quadrantDiagram-34T5L4WZ-TuVP5Gi6.js → quadrantDiagram-34T5L4WZ-DIjTM3lm.js} +1 -1
  49. package/dist/dashboard/assets/{requirementDiagram-MS252O5E-D3mS8_ke.js → requirementDiagram-MS252O5E-B9AEoGYh.js} +1 -1
  50. package/dist/dashboard/assets/{sankeyDiagram-XADWPNL6-BmVRers6.js → sankeyDiagram-XADWPNL6-C_O5XLXm.js} +1 -1
  51. package/dist/dashboard/assets/{sequenceDiagram-FGHM5R23-CTYCgk5P.js → sequenceDiagram-FGHM5R23-CHSxSvmJ.js} +1 -1
  52. package/dist/dashboard/assets/{stateDiagram-FHFEXIEX-DNbP2aCg.js → stateDiagram-FHFEXIEX-B0URrhHw.js} +1 -1
  53. package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-FdAb9MBo.js +1 -0
  54. package/dist/dashboard/assets/{timeline-definition-GMOUNBTQ-DKHJd4z8.js → timeline-definition-GMOUNBTQ-CycU2gVC.js} +1 -1
  55. package/dist/dashboard/assets/{vennDiagram-DHZGUBPP-ChotNMNq.js → vennDiagram-DHZGUBPP-DcU2536G.js} +1 -1
  56. package/dist/dashboard/assets/{wardley-RL74JXVD-Dmo2_ksP.js → wardley-RL74JXVD-BWqqaX3S.js} +1 -1
  57. package/dist/dashboard/assets/{wardleyDiagram-NUSXRM2D-Z6Wbiv2H.js → wardleyDiagram-NUSXRM2D-CUnIHJgd.js} +1 -1
  58. package/dist/dashboard/assets/{xychartDiagram-5P7HB3ND-BJa0AHtN.js → xychartDiagram-5P7HB3ND-b9fcOYTj.js} +1 -1
  59. package/dist/dashboard/index.html +2 -2
  60. package/dist/dashboard/media/README.md +29 -0
  61. package/dist/dashboard/media/brain-hero.png +0 -0
  62. package/dist/dashboard/media/brain.mp4 +0 -0
  63. package/dist/dashboard/media/brain.webm +0 -0
  64. package/dist/dashboard/media/shot-disabled.png +0 -0
  65. package/dist/dashboard/media/shot-enabled.png +0 -0
  66. package/dist/index.js +2280 -1568
  67. package/dist/skill-packs/catalog.json +44 -0
  68. package/dist/skill-packs/excalidraw/SKILL.md +123 -0
  69. package/dist/skill-packs/excalidraw/examples/hello.spec.json +14 -0
  70. package/dist/skill-packs/excalidraw/examples/sample.png +0 -0
  71. package/dist/skill-packs/excalidraw/examples/style_board.js +31 -0
  72. package/dist/skill-packs/excalidraw/package.json +5 -0
  73. package/dist/skill-packs/excalidraw/reference/format.md +93 -0
  74. package/dist/skill-packs/excalidraw/scripts/build_excalidraw.js +357 -0
  75. package/dist/skill-packs/excalidraw/scripts/lib/fractional-indexing.LICENSE +121 -0
  76. package/dist/skill-packs/excalidraw/scripts/lib/fractional-indexing.js +311 -0
  77. package/dist/skill-packs/excalidraw/scripts/lib/imagesize.js +65 -0
  78. package/dist/skill-packs/excalidraw/scripts/lib/style.js +130 -0
  79. package/dist/skill-packs/video-watching/SKILL.md +151 -0
  80. package/dist/skill-packs/video-watching/scripts/build_frame_index.py +95 -0
  81. package/dist/skill-packs/video-watching/scripts/gap_fill.py +45 -0
  82. package/dist/skill-packs/video-watching/scripts/transcribe.sh +136 -0
  83. package/package.json +4 -2
  84. package/skill/SKILL.md +5 -0
  85. package/skill-packs/catalog.json +44 -0
  86. package/skill-packs/excalidraw/SKILL.md +123 -0
  87. package/skill-packs/excalidraw/examples/hello.spec.json +14 -0
  88. package/skill-packs/excalidraw/examples/sample.png +0 -0
  89. package/skill-packs/excalidraw/examples/style_board.js +31 -0
  90. package/skill-packs/excalidraw/package.json +5 -0
  91. package/skill-packs/excalidraw/reference/format.md +93 -0
  92. package/skill-packs/excalidraw/scripts/build_excalidraw.js +357 -0
  93. package/skill-packs/excalidraw/scripts/lib/fractional-indexing.LICENSE +121 -0
  94. package/skill-packs/excalidraw/scripts/lib/fractional-indexing.js +311 -0
  95. package/skill-packs/excalidraw/scripts/lib/imagesize.js +65 -0
  96. package/skill-packs/excalidraw/scripts/lib/style.js +130 -0
  97. package/skill-packs/video-watching/SKILL.md +151 -0
  98. package/skill-packs/video-watching/scripts/build_frame_index.py +95 -0
  99. package/skill-packs/video-watching/scripts/gap_fill.py +45 -0
  100. package/skill-packs/video-watching/scripts/transcribe.sh +136 -0
  101. package/dist/dashboard/assets/channel-CbuTKKsM.js +0 -1
  102. package/dist/dashboard/assets/classDiagram-6PBFFD2Q-HXl3A_sM.js +0 -1
  103. package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-HXl3A_sM.js +0 -1
  104. package/dist/dashboard/assets/clone-NKHOtdi8.js +0 -1
  105. package/dist/dashboard/assets/index-DK_2-eWY.css +0 -1
  106. package/dist/dashboard/assets/index-Ds-JXWtr.js +0 -476
  107. package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-CfN6kjo8.js +0 -1
@@ -352,6 +352,50 @@
352
352
  "security"
353
353
  ],
354
354
  "alwaysApply": false
355
+ },
356
+ {
357
+ "name": "excalidraw",
358
+ "file": "excalidraw/SKILL.md",
359
+ "description": "Generate or extend Obsidian Excalidraw (.excalidraw.md) boards — lay out images/screenshots, text, shapes, arrows, frames, lanes and grids — by writing a small JSON spec and running a deterministic script that emits valid plugin markup (near-zero tokens, always renders). Embeds local images via the plugin's sha1 wikilink trick (no base64). Triggers: 'draw this in excalidraw', 'make an excalidraw board', 'excalidraw diagram', 'funnel map board', 'embed screenshots into excalidraw', 'excalidraw çiz', 'excalidraw board oluştur'.",
360
+ "tags": [
361
+ "excalidraw",
362
+ "obsidian",
363
+ "diagram",
364
+ "board",
365
+ "visualization",
366
+ "screenshots",
367
+ "funnel-map",
368
+ "drawing"
369
+ ],
370
+ "alwaysApply": false,
371
+ "bundleDir": true
372
+ },
373
+ {
374
+ "name": "video-watching",
375
+ "file": "video-watching/SKILL.md",
376
+ "description": "Load when the user hands over a video file/link or asks to watch/analyze a video, extract a transcript, or 'bu videoda ne var'. Turns a video into a time-mapped transcript with on-screen visuals (slides, UI, charts, text) described inline, then reasons about it. Triggers: 'watch this video', 'videoyu izle/analiz et', 'transkript çıkar', 'video transcript'.",
377
+ "tags": [
378
+ "video",
379
+ "transcript",
380
+ "whisper",
381
+ "ffmpeg",
382
+ "frames",
383
+ "ocr",
384
+ "marketing-teardown",
385
+ "media"
386
+ ],
387
+ "alwaysApply": false,
388
+ "bundleDir": true,
389
+ "requiresBinaries": [
390
+ "whisper-cpp",
391
+ "ffmpeg",
392
+ "yt-dlp (remote links only)"
393
+ ],
394
+ "crossPackDeps": [
395
+ "growth (marketing-video teardown handoff)",
396
+ "meta-marketing (ad-creative teardown handoff)",
397
+ "design/onboarding-design (app-demo / FTE reconstruction handoff)"
398
+ ]
355
399
  }
356
400
  ],
357
401
  "agents": [
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: excalidraw
3
+ description: >-
4
+ Generate or extend Obsidian Excalidraw (.excalidraw.md) boards in this vault — lay out
5
+ images/screenshots, text labels, shapes, arrows, frames, lanes and grids — by writing a small
6
+ JSON spec and letting a deterministic script emit valid plugin markup (so it costs ~no tokens and
7
+ always renders). Embeds local images via the plugin's sha1 wikilink trick (no base64). Triggers:
8
+ '/excalidraw', 'draw this in excalidraw', 'make an excalidraw board', 'excalidraw diagram',
9
+ 'funnel map board', 'embed screenshots into excalidraw', 'add images to an excalidraw file',
10
+ 'excalidraw çiz', 'excalidraw board oluştur', 'ekran görüntülerini excalidraw a ekle'.
11
+ ---
12
+
13
+ # Excalidraw board generator (Obsidian)
14
+
15
+ Write `.excalidraw.md` files that render natively in this vault's Obsidian Excalidraw plugin.
16
+ **Do not hand-author the scene JSON.** Build a small spec and run the generator — it produces the
17
+ frontmatter, `## Text Elements`, `## Embedded Files`, and the `%% ## Drawing … %%` JSON, with
18
+ correct sha1 image links, fractional z-indices, and deterministic seeds (clean git diffs).
19
+
20
+ ## The one trick that makes images work
21
+ An image element references `fileId` = **sha1 of the image file**. The `## Embedded Files` section
22
+ maps `<sha1>: [[image.png]]`. The plugin resolves the picture from the vault via that wikilink —
23
+ **no base64 needed.** The generator computes the sha1 and writes both sides for you.
24
+
25
+ ## Usage
26
+
27
+ ### CLI (most common)
28
+ ```bash
29
+ node .claude/skills/excalidraw/scripts/build_excalidraw.js <spec.json> [--out <path.excalidraw.md>]
30
+ ```
31
+ Write a spec JSON, then run it. The script prints `elements/images/texts` counts on success.
32
+
33
+ ### JS API (for pipelines that generate many boards)
34
+ ```js
35
+ const { buildExcalidraw, lane, grid } = require('.../scripts/build_excalidraw.js');
36
+ buildExcalidraw({ out, elements: [ ...lane({ title, images, x, y, thumbW }) ] });
37
+ ```
38
+
39
+ ## Spec schema
40
+ ```jsonc
41
+ {
42
+ "out": "/abs/path/Board.excalidraw.md", // required (or pass --out)
43
+ "vaultRoot": "/abs/vault", // optional; auto-detected by walking up to `.obsidian`
44
+ "attachDir": "Attachments", // external images get copied here (relative to board dir)
45
+ "wikilinkMode": "basename", // "basename" (default) or "path" (vault-relative)
46
+ "background": "#ffffff",
47
+ "elements": [ /* ElementSpec… */ ]
48
+ }
49
+ ```
50
+
51
+ ### Element types
52
+ - `text` — `{ x, y, text, fontSize?, color?, width?, align?, fontFamily? }` (fontFamily 1=hand, 2=normal, 3=code). **Set `width` for any caption/label that must stay inside a column or card** → the text WRAPS to that width (autoResize off) and its height is computed from the wrapped line count. Omit `width` only for short single-line text you want sized to content (it renders on one line and will overlap neighbours if long).
53
+ - `image` — `{ x, y, path, width? , height? }` — give ONE of width/height; the other is derived from aspect. `path` is an absolute file path.
54
+ - `rectangle`/`ellipse`/`diamond` — `{ x, y, width, height, strokeColor?, backgroundColor?, fillStyle?(solid|hachure|cross-hatch), strokeWidth?, roundness?(true=rounded) }`
55
+ - `line`/`arrow` — `{ points: [[x1,y1],[x2,y2],…], strokeColor?, strokeWidth?, endArrow?, startArrow? }` (coords are absolute; arrows default to an end arrowhead)
56
+ - `frame` — `{ x, y, width, height, name }`
57
+
58
+ ### Layout helpers (JS API) — dimension-aware, never gap/overlap
59
+ Both read each image's real pixel size at layout time, so every caption **hugs its own image's bottom**
60
+ (no fixed-aspect guess → no big gap under a short screenshot, no overlap under a tall one). They return
61
+ an `ElementSpec[]` you spread with `...`, and that array also carries `.height`, `.width`, and **`.nextY`**
62
+ so you can stack lanes/grids/sections without doing height math by hand.
63
+ - `lane({ title, images:[{path, caption}], x, y, thumbW, gap, captionSize, captionGap })` → titled horizontal
64
+ strip; caption wraps to `thumbW` under each thumb. Ideal for **funnel step maps** (one lane per funnel).
65
+ - `grid({ images:[{path, caption}], x, y, cols, thumbW, gapX, gapY, captionSize })` → wrapping grid; each
66
+ row sizes to its tallest image, each caption hugs its image.
67
+ - `thumbHeight(path, thumbW)` → the on-board height a thumbnail of width `thumbW` will get (exported for
68
+ custom layouts that need to place captions/rows themselves).
69
+ ```js
70
+ const a = lane({ title:'Pilates', images, x:60, y:120, thumbW:150 });
71
+ const b = lane({ title:'Fasting', images2, x:60, y:a.nextY, thumbW:150 }); // stacks under `a`, no overlap
72
+ buildExcalidraw({ out, elements:[ ...a, ...b ] });
73
+ ```
74
+ House-style `card()` labels are also vertically centered correctly even when the label **wraps** to the card width.
75
+
76
+ ## House style (`scripts/lib/style.js`) — learned from the vault's presentation boards
77
+ Match the team's look by default. The builder now defaults text to **Excalifont (fontFamily 5)**.
78
+ `style.js` exports the palette + ready-made builders so boards look hand-drawn and consistent:
79
+
80
+ - **`PALETTE`** (semantic, = Excalidraw's native swatches): `green` benefit/go · `red` pain/risk ·
81
+ `blue` system/flow · `purple` core service · `yellow` processing · `mint` result · `gray` muted.
82
+ Each is `{ fill, stroke }`. `INK` = `#1e1e1e` (the default 2px card outline). Fills are **solid**,
83
+ rects are **always rounded**.
84
+ - **`card({x,y,w,h,text,color,fontSize})`** → rounded filled rect + centered label, grouped (move as one).
85
+ - **`node(...)`** → smaller card for flow steps.
86
+ - **`sectionTitle({x,y,text,fontSize})`** → big plain header (no box), like "Configuration" / "Flow".
87
+ - **`connector({from,to,label,double,dashed})`** → labeled arrow between two points.
88
+ - **`annotate({from,to,text})`** → short arrow to a side-spec note (the "90sec / deepseek…" pattern).
89
+ - **`column({x,y,items,color})`** → vertical stack of cards (benefit/risk lists). `items` may be
90
+ strings or `{text,color}`.
91
+ - **`hub({cx,cy,label,spokes})`** → central node + satellites with arrows (the "Layer + APIs" pattern).
92
+ - **`bullets({x,y,items})`** → one left-aligned bulleted text block (their spec-list pattern).
93
+ - Edge helpers `center/rightOf/leftOf/topOf/bottomOf(x,y,w,h)` to wire connectors to card edges.
94
+
95
+ Compose these into a `buildExcalidraw({ out, elements })` call. Runnable example: `examples/style_board.js`.
96
+ Conventions: color nodes by ROLE, label every arrow, put specs in side-annotations, use big plain
97
+ section titles to break the board into "slides", keep one idea per card.
98
+
99
+ ## Rules & gotchas
100
+ - **Unique image filenames.** Obsidian resolves `[[name.png]]` by basename; if two different files
101
+ share a name anywhere in the vault the link is ambiguous. Name screenshots uniquely
102
+ (e.g. `bm-pilates-2707-step-03.png`) or use `"wikilinkMode": "path"`.
103
+ - Images must live inside the vault. External paths are auto-copied into `attachDir`.
104
+ - Output is uncompressed JSON inside `%% … %%`. The plugin reads it; on its first save it may
105
+ re-compress — that's expected and harmless.
106
+ - Re-running with the same `out` + same spec yields a byte-stable file (seeds are derived from the
107
+ output path), so boards diff cleanly in git.
108
+ - **Self-contained / portable.** No `npm install` — `build_excalidraw.js` uses only Node builtins +
109
+ the vendored `scripts/lib/*` (fractional-indexing, imagesize, style). Copy `.claude/skills/excalidraw/`
110
+ into any project and it works. `vaultRoot` auto-detects `.obsidian`; with none it falls back to the
111
+ board's own folder, so the `.excalidraw.md` is still written (it just renders inside Obsidian).
112
+
113
+ ## Verify
114
+ Run the bundled examples from the skill folder (they use skill-relative paths + a bundled `sample.png`,
115
+ so they work in any project):
116
+ ```bash
117
+ cd .claude/skills/excalidraw
118
+ node scripts/build_excalidraw.js examples/hello.spec.json # spec-driven: text/shapes/image → examples/Hello Excalidraw.excalidraw.md
119
+ node examples/style_board.js # JS API: card/connector/lane house style → examples/Style Demo.excalidraw.md
120
+ ```
121
+ Then open the resulting board in Obsidian (Excalidraw view) to confirm it renders.
122
+
123
+ See `reference/format.md` for the exact `.excalidraw.md` anatomy reverse-engineered from this vault.
@@ -0,0 +1,14 @@
1
+ {
2
+ "out": "examples/Hello Excalidraw.excalidraw.md",
3
+ "background": "#ffffff",
4
+ "elements": [
5
+ { "type": "text", "x": 0, "y": -70, "text": "excalidraw skill — portable smoke test", "fontSize": 28, "color": "#1971c2" },
6
+ { "type": "text", "x": 0, "y": -28, "text": "Give a text a width and it wraps to that width (no overlap). The image embeds via sha1 wikilink — no base64. Only skill-relative paths, so this runs in any project.", "fontSize": 15, "color": "#868e96", "width": 540 },
7
+ { "type": "rectangle", "x": 0, "y": 40, "width": 260, "height": 140, "strokeColor": "#1e1e1e", "backgroundColor": "#a5d8ff", "fillStyle": "solid", "strokeWidth": 2, "roundness": true },
8
+ { "type": "text", "x": 24, "y": 96, "text": "rounded rect\n+ fill", "fontSize": 20, "color": "#1e1e1e" },
9
+ { "type": "ellipse", "x": 340, "y": 40, "width": 160, "height": 140, "strokeColor": "#2f9e44", "backgroundColor": "#b2f2bb", "fillStyle": "hachure" },
10
+ { "type": "arrow", "points": [[260, 110], [340, 110]], "strokeColor": "#e8590c", "strokeWidth": 3 },
11
+ { "type": "text", "x": 0, "y": 220, "text": "embedded image (resolved from vault by sha1 wikilink):", "fontSize": 16, "color": "#555", "width": 420 },
12
+ { "type": "image", "x": 0, "y": 250, "path": "examples/sample.png", "width": 320 }
13
+ ]
14
+ }
@@ -0,0 +1,31 @@
1
+ // style_board.js — house-style demo. Run from anywhere: node examples/style_board.js
2
+ // Shows card/connector/sectionTitle + the dimension-aware lane() (captions hug images, .nextY stacks).
3
+ const path = require('path');
4
+ const { buildExcalidraw, lane } = require(path.join(__dirname, '..', 'scripts', 'build_excalidraw.js'));
5
+ const { card, connector, sectionTitle, rightOf, leftOf, bottomOf, topOf } = require(path.join(__dirname, '..', 'scripts', 'lib', 'style.js'));
6
+
7
+ const SAMPLE = path.join(__dirname, 'sample.png');
8
+ const els = [];
9
+ const P = (...a) => els.push(...a.flat());
10
+
11
+ // --- flow row with labeled connectors (color nodes by role) ---
12
+ P(sectionTitle({ x: 0, y: 0, text: 'House style', fontSize: 40 }));
13
+ const A = { x: 0, y: 90, w: 220, h: 84 }, B = { x: 360, y: 90, w: 220, h: 84 }, C = { x: 720, y: 90, w: 220, h: 84 };
14
+ P(card({ ...A, text: 'input', color: 'blue' }));
15
+ P(card({ ...B, text: 'core service', color: 'purple' }));
16
+ P(card({ ...C, text: 'result', color: 'mint' }));
17
+ P(connector({ from: rightOf(A.x, A.y, A.w, A.h), to: leftOf(B.x, B.y, B.w, B.h), label: 'request' }));
18
+ P(connector({ from: rightOf(B.x, B.y, B.w, B.h), to: leftOf(C.x, C.y, C.w, C.h), label: 'response' }));
19
+
20
+ // --- a dimension-aware lane: captions hug each image, .nextY gives the next free row ---
21
+ const imgs = [
22
+ { path: SAMPLE, caption: 'lane() reads the real image size, so this caption hugs the bottom and wraps to the thumb width — no gap, no overlap.' },
23
+ { path: SAMPLE, caption: 'second thumb' },
24
+ { path: SAMPLE, caption: 'third thumb' },
25
+ ];
26
+ const strip = lane({ title: 'lane() — funnel-step style strip', images: imgs, x: 0, y: 220, thumbW: 200 });
27
+ P(strip);
28
+ P(sectionTitle({ x: 0, y: strip.nextY, text: 'stacked below via strip.nextY', fontSize: 22, color: '#868e96' }));
29
+
30
+ buildExcalidraw({ out: path.join(__dirname, 'Style Demo.excalidraw.md'), elements: els, background: '#fbfbfb' });
31
+ console.log('style demo →', path.join(__dirname, 'Style Demo.excalidraw.md'));
@@ -0,0 +1,5 @@
1
+ {
2
+ "type": "commonjs",
3
+ "private": true,
4
+ "description": "Scopes the excalidraw skill's vendored scripts to CommonJS so `require()` works even when the host project sets \"type\": \"module\". No dependencies — scripts use only Node builtins + vendored lib/."
5
+ }
@@ -0,0 +1,93 @@
1
+ # Obsidian `.excalidraw.md` format (reverse-engineered from this vault)
2
+
3
+ Source files inspected: `lina/excalidraw/Lina Web Funnel.excalidraw.md` (decompressed),
4
+ `Lina Web Funnel Benchmark.excalidraw.md`. Plugin: `zsviczian/obsidian-excalidraw-plugin`.
5
+
6
+ ## File anatomy
7
+ ```
8
+ ---
9
+
10
+ excalidraw-plugin: parsed
11
+ tags: [excalidraw]
12
+
13
+ ---
14
+ ==⚠ Switch to EXCALIDRAW VIEW … ⚠== …saving hint…
15
+
16
+
17
+ # Excalidraw Data
18
+
19
+ ## Text Elements
20
+ <text content> ^<elementId> ← one block per text element, blank line between
21
+
22
+ ## Embedded Files
23
+ <sha1>: [[image filename.png]] ← maps fileId → vault image, blank line between
24
+
25
+ %%
26
+ ## Drawing
27
+ ```json
28
+ { "type":"excalidraw", "version":2, "source":"…", "elements":[…], "appState":{…}, "files":{} }
29
+ ```
30
+ %%
31
+ ```
32
+ - `## Drawing` may be ` ```compressed-json ` (LZString.compressToBase64) **or** ` ```json `
33
+ (uncompressed). The plugin reads both. We emit uncompressed JSON — readable, no deps.
34
+ - The whole drawing block is wrapped in `%% … %%` (Obsidian comment) so it hides in reading view.
35
+ - `## Text Elements` and `## Embedded Files` sit **before** the `%%`.
36
+
37
+ ## Image embedding (the key mechanism)
38
+ - An image element is `{"type":"image", "fileId":"<sha1>", …, "scale":[1,1], "status":"pending", "crop":null}`.
39
+ - `fileId` is the **sha1 of the image file's bytes**.
40
+ - The `## Embedded Files` line `<sha1>: [[file.png]]` is what the plugin uses to load the picture
41
+ from the vault. **The scene `files` object can be empty (`{}`)** — no base64 dataURL required.
42
+ - `crop` is optional (only when the user cropped). `null` shows the whole image.
43
+
44
+ ## Ground-truth image element (decompressed from the vault)
45
+ ```json
46
+ {
47
+ "id":"GtJwnnEj","type":"image","x":-147.3,"y":-290.5,"width":118.8,"height":469,
48
+ "angle":0,"strokeColor":"transparent","backgroundColor":"transparent","fillStyle":"solid",
49
+ "strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,
50
+ "index":"a0","roundness":null,"seed":2054545386,"version":188,"versionNonce":1261541290,
51
+ "isDeleted":false,"boundElements":[],"updated":1779266944741,"link":null,"locked":false,
52
+ "status":"pending","fileId":"3eaa974da433f70ba06cb3fb2587b486d6143d32","scale":[1,1],
53
+ "crop":{ "x":713.7,"y":0,"width":871.3,"height":3440,"naturalWidth":2283,"naturalHeight":3440 },
54
+ "hasTextLink":false
55
+ }
56
+ ```
57
+ …and its `## Embedded Files` line:
58
+ ```
59
+ 3eaa974da433f70ba06cb3fb2587b486d6143d32: [[Pasted Image 20260515130812_941.png]]
60
+ ```
61
+
62
+ ## z-order: fractional indices
63
+ - Every element has a string `index` (`"a0"`, `"a1"`, …) — fractional indexing, compared
64
+ lexicographically. We generate them with the vendored `fractional-indexing` lib
65
+ (`generateNKeysBetween(null,null,N)`), which matches the vault output exactly.
66
+ - Excalidraw's `restore()` (run by the plugin on load) repairs invalid/missing indices, but we emit
67
+ valid ascending ones anyway.
68
+
69
+ ## appState
70
+ Minimal is fine: `{"gridSize":null,"gridStep":5,"gridModeEnabled":false,"viewBackgroundColor":"#ffffff"}`.
71
+
72
+ ## House style (measured from MemoryOS High Level / Personalization Module / Research)
73
+ The vault's presentation boards share one fingerprint — `scripts/lib/style.js` encodes it:
74
+ - **Font:** Excalifont, `fontFamily: 5`, for ~100% of text. Sizes form a scale: ~16 labels, ~20 body,
75
+ ~28 sub-headers, ~44–50 section titles (scaled up on large zoomed-out canvases).
76
+ - **Fills:** `fillStyle: "solid"` exclusively (never hachure). **Every rectangle is rounded.**
77
+ - **Ink:** `strokeColor: "#1e1e1e"`, `strokeWidth: 2` dominant (1 for fine/secondary).
78
+ - **Semantic palette** (Excalidraw native swatches): green `#b2f2bb`/`#2f9e44` (benefit/go),
79
+ red `#ffc9c9`/`#e03131` (pain/risk), blue `#a5d8ff`/`#1971c2` (system/flow), purple `#d0bfff`/`#6741d9`
80
+ (core service), yellow `#ffec99`/`#f08c00` (processing), mint `#96f2d7`/`#0c8599` (result),
81
+ pale variants `#ebfbee`/`#e7f5ff`/`#f3f0ff` for backdrops.
82
+ - **Patterns:** rounded-card (rect + centered label, grouped) · color nodes by role · ellipse = actor,
83
+ diamond = decision, container-rect groups sub-states · arrows almost always single end-arrowhead,
84
+ labeled inline ("req", "msg+ctx") · side-annotation arrows to spec notes · big plain section titles
85
+ ("Configuration", "Flow") · benefit/risk **card columns** · hub-and-spoke concept maps · bulleted
86
+ spec lists as one text block. Heavy use of embedded screenshots in research murals (huge canvases,
87
+ 29+ groups) — exactly the funnel-map use case.
88
+
89
+ ## Tooling note
90
+ The `yctimlin/mcp_excalidraw` MCP server was evaluated and rejected for this vault: it needs a
91
+ running localhost canvas server and exports excalidraw.com JSON / PNG / share-URLs — it does **not**
92
+ write Obsidian `.excalidraw.md` with the `## Embedded Files` wikilink section. Hand-generating the
93
+ file (this skill) is the correct, dependency-light path for bulk-embedding local screenshots.