dreamcontext 0.5.4 → 0.7.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 (108) hide show
  1. package/LICENSE +201 -21
  2. package/NOTICE +7 -0
  3. package/README.md +79 -13
  4. package/agents/sleep-product.md +48 -10
  5. package/agents/sleep-state.md +43 -22
  6. package/agents/sleep-tasks.md +62 -9
  7. package/dist/agents/sleep-product.md +48 -10
  8. package/dist/agents/sleep-state.md +43 -22
  9. package/dist/agents/sleep-tasks.md +62 -9
  10. package/dist/dashboard/assets/{BrainCanvas3D-1HMiA14F.js → BrainCanvas3D-lFgJbbhZ.js} +1 -1
  11. package/dist/dashboard/assets/{_baseUniq-C7pfRmXz.js → _baseUniq-BpANgc_i.js} +1 -1
  12. package/dist/dashboard/assets/{arc-B5x_GF9Q.js → arc-CX32Jm7E.js} +1 -1
  13. package/dist/dashboard/assets/{architectureDiagram-Q4EWVU46-CWmjaqno.js → architectureDiagram-Q4EWVU46-ARASlGxO.js} +1 -1
  14. package/dist/dashboard/assets/{blockDiagram-DXYQGD6D-B7SyUIdV.js → blockDiagram-DXYQGD6D-BBvsYm9E.js} +1 -1
  15. package/dist/dashboard/assets/{c4Diagram-AHTNJAMY-OeJBWXHU.js → c4Diagram-AHTNJAMY-ChSUfXR9.js} +1 -1
  16. package/dist/dashboard/assets/channel-w_Bp182N.js +1 -0
  17. package/dist/dashboard/assets/{chunk-4BX2VUAB-2Oop1B0M.js → chunk-4BX2VUAB-COHoVEpt.js} +1 -1
  18. package/dist/dashboard/assets/{chunk-4TB4RGXK-Bh7qSu_X.js → chunk-4TB4RGXK-DKJFLaTC.js} +1 -1
  19. package/dist/dashboard/assets/{chunk-55IACEB6-gNeHRO_i.js → chunk-55IACEB6-CKZPWZRu.js} +1 -1
  20. package/dist/dashboard/assets/{chunk-EDXVE4YY-BRniAH53.js → chunk-EDXVE4YY-k1y4mGUJ.js} +1 -1
  21. package/dist/dashboard/assets/{chunk-FMBD7UC4-B5r8U2gJ.js → chunk-FMBD7UC4-ixXe5R10.js} +1 -1
  22. package/dist/dashboard/assets/{chunk-OYMX7WX6-Dd1c17fm.js → chunk-OYMX7WX6-NDFap7xg.js} +1 -1
  23. package/dist/dashboard/assets/{chunk-QZHKN3VN-BIw-OdTn.js → chunk-QZHKN3VN-vY0UpHjB.js} +1 -1
  24. package/dist/dashboard/assets/{chunk-YZCP3GAM-Dl9MlwCW.js → chunk-YZCP3GAM-yztvsKR-.js} +1 -1
  25. package/dist/dashboard/assets/classDiagram-6PBFFD2Q-CcQxcqy9.js +1 -0
  26. package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-CcQxcqy9.js +1 -0
  27. package/dist/dashboard/assets/clone-_znoR_ci.js +1 -0
  28. package/dist/dashboard/assets/{cose-bilkent-S5V4N54A-DlrROH5K.js → cose-bilkent-S5V4N54A-DKXM5Fh5.js} +1 -1
  29. package/dist/dashboard/assets/{dagre-KV5264BT-B0GpPXKL.js → dagre-KV5264BT-DN1Nlsmy.js} +1 -1
  30. package/dist/dashboard/assets/{diagram-5BDNPKRD-BBYcDW2Z.js → diagram-5BDNPKRD-DnoJFRqR.js} +1 -1
  31. package/dist/dashboard/assets/{diagram-G4DWMVQ6-BxHxugdp.js → diagram-G4DWMVQ6-CF1_jTNI.js} +1 -1
  32. package/dist/dashboard/assets/{diagram-MMDJMWI5-C9Jc19Wo.js → diagram-MMDJMWI5-D4-bZZEt.js} +1 -1
  33. package/dist/dashboard/assets/{diagram-TYMM5635-Vmo5GAAU.js → diagram-TYMM5635-al8RqVgf.js} +1 -1
  34. package/dist/dashboard/assets/{erDiagram-SMLLAGMA-pskr9x-V.js → erDiagram-SMLLAGMA-MEcC0rwO.js} +1 -1
  35. package/dist/dashboard/assets/{flowDiagram-DWJPFMVM-B1D7Ky0W.js → flowDiagram-DWJPFMVM-DCLyNpW8.js} +1 -1
  36. package/dist/dashboard/assets/{ganttDiagram-T4ZO3ILL-BVJZZV1M.js → ganttDiagram-T4ZO3ILL-DfEvbbJK.js} +1 -1
  37. package/dist/dashboard/assets/{gitGraphDiagram-UUTBAWPF-BiOGN6We.js → gitGraphDiagram-UUTBAWPF-C_YozdVL.js} +1 -1
  38. package/dist/dashboard/assets/{graph-D58deEXr.js → graph-DpIXS1G1.js} +1 -1
  39. package/dist/dashboard/assets/index-Bo5CUa_M.js +480 -0
  40. package/dist/dashboard/assets/index-DrqurW1c.css +1 -0
  41. package/dist/dashboard/assets/{infoDiagram-42DDH7IO-DgJYg1X1.js → infoDiagram-42DDH7IO-AdT6kjzj.js} +1 -1
  42. package/dist/dashboard/assets/{ishikawaDiagram-UXIWVN3A-arJgr-8P.js → ishikawaDiagram-UXIWVN3A-B0_9IZVO.js} +1 -1
  43. package/dist/dashboard/assets/{journeyDiagram-VCZTEJTY-B3DCvb8p.js → journeyDiagram-VCZTEJTY-BveNBswQ.js} +1 -1
  44. package/dist/dashboard/assets/{kanban-definition-6JOO6SKY-B--dN6Ma.js → kanban-definition-6JOO6SKY-CFI8j4jR.js} +1 -1
  45. package/dist/dashboard/assets/{layout-z2qEbZWC.js → layout-DI7XjZy1.js} +1 -1
  46. package/dist/dashboard/assets/{linear-BQu1dwrM.js → linear-CSjp56iw.js} +1 -1
  47. package/dist/dashboard/assets/{min-DkF5b2sh.js → min-pAGUJmEC.js} +1 -1
  48. package/dist/dashboard/assets/{mindmap-definition-QFDTVHPH-Bw04koyq.js → mindmap-definition-QFDTVHPH-C2mBnknr.js} +1 -1
  49. package/dist/dashboard/assets/{pieDiagram-DEJITSTG-vNqz64o8.js → pieDiagram-DEJITSTG-i_phWDqD.js} +1 -1
  50. package/dist/dashboard/assets/{quadrantDiagram-34T5L4WZ-TuVP5Gi6.js → quadrantDiagram-34T5L4WZ-Dv7TGJjw.js} +1 -1
  51. package/dist/dashboard/assets/{requirementDiagram-MS252O5E-D3mS8_ke.js → requirementDiagram-MS252O5E-CB2Jl-O5.js} +1 -1
  52. package/dist/dashboard/assets/{sankeyDiagram-XADWPNL6-BmVRers6.js → sankeyDiagram-XADWPNL6-DxCoN-EI.js} +1 -1
  53. package/dist/dashboard/assets/{sequenceDiagram-FGHM5R23-CTYCgk5P.js → sequenceDiagram-FGHM5R23-BeZyaehJ.js} +1 -1
  54. package/dist/dashboard/assets/{stateDiagram-FHFEXIEX-DNbP2aCg.js → stateDiagram-FHFEXIEX-D3AjQzD1.js} +1 -1
  55. package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-Ci9v--xK.js +1 -0
  56. package/dist/dashboard/assets/{timeline-definition-GMOUNBTQ-DKHJd4z8.js → timeline-definition-GMOUNBTQ-3l_8RFUg.js} +1 -1
  57. package/dist/dashboard/assets/{vennDiagram-DHZGUBPP-ChotNMNq.js → vennDiagram-DHZGUBPP-2QiY25JD.js} +1 -1
  58. package/dist/dashboard/assets/{wardley-RL74JXVD-Dmo2_ksP.js → wardley-RL74JXVD-DNLmFofz.js} +1 -1
  59. package/dist/dashboard/assets/{wardleyDiagram-NUSXRM2D-Z6Wbiv2H.js → wardleyDiagram-NUSXRM2D-BI0tupiS.js} +1 -1
  60. package/dist/dashboard/assets/{xychartDiagram-5P7HB3ND-BJa0AHtN.js → xychartDiagram-5P7HB3ND-CghXPE7_.js} +1 -1
  61. package/dist/dashboard/index.html +2 -2
  62. package/dist/dashboard/media/README.md +29 -0
  63. package/dist/dashboard/media/brain-hero.png +0 -0
  64. package/dist/dashboard/media/brain.mp4 +0 -0
  65. package/dist/dashboard/media/brain.webm +0 -0
  66. package/dist/dashboard/media/shot-disabled.png +0 -0
  67. package/dist/dashboard/media/shot-enabled.png +0 -0
  68. package/dist/index.js +3352 -2035
  69. package/dist/skill-packs/catalog.json +44 -0
  70. package/dist/skill-packs/excalidraw/SKILL.md +202 -0
  71. package/dist/skill-packs/excalidraw/examples/hello.spec.json +14 -0
  72. package/dist/skill-packs/excalidraw/examples/sample.png +0 -0
  73. package/dist/skill-packs/excalidraw/examples/style_board.js +31 -0
  74. package/dist/skill-packs/excalidraw/package.json +5 -0
  75. package/dist/skill-packs/excalidraw/reference/format.md +93 -0
  76. package/dist/skill-packs/excalidraw/scripts/build_excalidraw.js +357 -0
  77. package/dist/skill-packs/excalidraw/scripts/lib/fractional-indexing.LICENSE +121 -0
  78. package/dist/skill-packs/excalidraw/scripts/lib/fractional-indexing.js +311 -0
  79. package/dist/skill-packs/excalidraw/scripts/lib/imagesize.js +65 -0
  80. package/dist/skill-packs/excalidraw/scripts/lib/style.js +130 -0
  81. package/dist/skill-packs/video-watching/SKILL.md +192 -0
  82. package/dist/skill-packs/video-watching/scripts/build_frame_index.py +146 -0
  83. package/dist/skill-packs/video-watching/scripts/transcribe.sh +229 -0
  84. package/dist/templates/init/data-structures/default.md +26 -24
  85. package/package.json +4 -2
  86. package/skill/SKILL.md +31 -12
  87. package/skill-packs/catalog.json +44 -0
  88. package/skill-packs/excalidraw/SKILL.md +202 -0
  89. package/skill-packs/excalidraw/examples/hello.spec.json +14 -0
  90. package/skill-packs/excalidraw/examples/sample.png +0 -0
  91. package/skill-packs/excalidraw/examples/style_board.js +31 -0
  92. package/skill-packs/excalidraw/package.json +5 -0
  93. package/skill-packs/excalidraw/reference/format.md +93 -0
  94. package/skill-packs/excalidraw/scripts/build_excalidraw.js +357 -0
  95. package/skill-packs/excalidraw/scripts/lib/fractional-indexing.LICENSE +121 -0
  96. package/skill-packs/excalidraw/scripts/lib/fractional-indexing.js +311 -0
  97. package/skill-packs/excalidraw/scripts/lib/imagesize.js +65 -0
  98. package/skill-packs/excalidraw/scripts/lib/style.js +130 -0
  99. package/skill-packs/video-watching/SKILL.md +192 -0
  100. package/skill-packs/video-watching/scripts/build_frame_index.py +146 -0
  101. package/skill-packs/video-watching/scripts/transcribe.sh +229 -0
  102. package/dist/dashboard/assets/channel-CbuTKKsM.js +0 -1
  103. package/dist/dashboard/assets/classDiagram-6PBFFD2Q-HXl3A_sM.js +0 -1
  104. package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-HXl3A_sM.js +0 -1
  105. package/dist/dashboard/assets/clone-NKHOtdi8.js +0 -1
  106. package/dist/dashboard/assets/index-DK_2-eWY.css +0 -1
  107. package/dist/dashboard/assets/index-Ds-JXWtr.js +0 -476
  108. package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-CfN6kjo8.js +0 -1
@@ -0,0 +1,311 @@
1
+ // License: CC0 (no rights reserved).
2
+
3
+ // This is based on https://observablehq.com/@dgreensp/implementing-fractional-indexing
4
+
5
+ const BASE_62_DIGITS =
6
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
7
+
8
+ // `a` may be empty string, `b` is null or non-empty string.
9
+ // `a < b` lexicographically if `b` is non-null.
10
+ // no trailing zeros allowed.
11
+ // digits is a string such as '0123456789' for base 10. Digits must be in
12
+ // ascending character code order!
13
+ /**
14
+ * @param {string} a
15
+ * @param {string | null | undefined} b
16
+ * @param {string} digits
17
+ * @returns {string}
18
+ */
19
+ function midpoint(a, b, digits) {
20
+ const zero = digits[0];
21
+ if (b != null && a >= b) {
22
+ throw new Error(a + " >= " + b);
23
+ }
24
+ if (a.slice(-1) === zero || (b && b.slice(-1) === zero)) {
25
+ throw new Error("trailing zero");
26
+ }
27
+ if (b) {
28
+ // remove longest common prefix. pad `a` with 0s as we
29
+ // go. note that we don't need to pad `b`, because it can't
30
+ // end before `a` while traversing the common prefix.
31
+ let n = 0;
32
+ while ((a[n] || zero) === b[n]) {
33
+ n++;
34
+ }
35
+ if (n > 0) {
36
+ return b.slice(0, n) + midpoint(a.slice(n), b.slice(n), digits);
37
+ }
38
+ }
39
+ // first digits (or lack of digit) are different
40
+ const digitA = a ? digits.indexOf(a[0]) : 0;
41
+ const digitB = b != null ? digits.indexOf(b[0]) : digits.length;
42
+ if (digitB - digitA > 1) {
43
+ const midDigit = Math.round(0.5 * (digitA + digitB));
44
+ return digits[midDigit];
45
+ } else {
46
+ // first digits are consecutive
47
+ if (b && b.length > 1) {
48
+ return b.slice(0, 1);
49
+ } else {
50
+ // `b` is null or has length 1 (a single digit).
51
+ // the first digit of `a` is the previous digit to `b`,
52
+ // or 9 if `b` is null.
53
+ // given, for example, midpoint('49', '5'), return
54
+ // '4' + midpoint('9', null), which will become
55
+ // '4' + '9' + midpoint('', null), which is '495'
56
+ return digits[digitA] + midpoint(a.slice(1), null, digits);
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * @param {string} int
63
+ * @return {void}
64
+ */
65
+
66
+ function validateInteger(int) {
67
+ if (int.length !== getIntegerLength(int[0])) {
68
+ throw new Error("invalid integer part of order key: " + int);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * @param {string} head
74
+ * @return {number}
75
+ */
76
+
77
+ function getIntegerLength(head) {
78
+ if (head >= "a" && head <= "z") {
79
+ return head.charCodeAt(0) - "a".charCodeAt(0) + 2;
80
+ } else if (head >= "A" && head <= "Z") {
81
+ return "Z".charCodeAt(0) - head.charCodeAt(0) + 2;
82
+ } else {
83
+ throw new Error("invalid order key head: " + head);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * @param {string} key
89
+ * @return {string}
90
+ */
91
+
92
+ function getIntegerPart(key) {
93
+ const integerPartLength = getIntegerLength(key[0]);
94
+ if (integerPartLength > key.length) {
95
+ throw new Error("invalid order key: " + key);
96
+ }
97
+ return key.slice(0, integerPartLength);
98
+ }
99
+
100
+ /**
101
+ * @param {string} key
102
+ * @param {string} digits
103
+ * @return {void}
104
+ */
105
+
106
+ function validateOrderKey(key, digits) {
107
+ if (key === "A" + digits[0].repeat(26)) {
108
+ throw new Error("invalid order key: " + key);
109
+ }
110
+ // getIntegerPart will throw if the first character is bad,
111
+ // or the key is too short. we'd call it to check these things
112
+ // even if we didn't need the result
113
+ const i = getIntegerPart(key);
114
+ const f = key.slice(i.length);
115
+ if (f.slice(-1) === digits[0]) {
116
+ throw new Error("invalid order key: " + key);
117
+ }
118
+ }
119
+
120
+ // note that this may return null, as there is a largest integer
121
+ /**
122
+ * @param {string} x
123
+ * @param {string} digits
124
+ * @return {string | null}
125
+ */
126
+ function incrementInteger(x, digits) {
127
+ validateInteger(x);
128
+ const [head, ...digs] = x.split("");
129
+ let carry = true;
130
+ for (let i = digs.length - 1; carry && i >= 0; i--) {
131
+ const d = digits.indexOf(digs[i]) + 1;
132
+ if (d === digits.length) {
133
+ digs[i] = digits[0];
134
+ } else {
135
+ digs[i] = digits[d];
136
+ carry = false;
137
+ }
138
+ }
139
+ if (carry) {
140
+ if (head === "Z") {
141
+ return "a" + digits[0];
142
+ }
143
+ if (head === "z") {
144
+ return null;
145
+ }
146
+ const h = String.fromCharCode(head.charCodeAt(0) + 1);
147
+ if (h > "a") {
148
+ digs.push(digits[0]);
149
+ } else {
150
+ digs.pop();
151
+ }
152
+ return h + digs.join("");
153
+ } else {
154
+ return head + digs.join("");
155
+ }
156
+ }
157
+
158
+ // note that this may return null, as there is a smallest integer
159
+ /**
160
+ * @param {string} x
161
+ * @param {string} digits
162
+ * @return {string | null}
163
+ */
164
+
165
+ function decrementInteger(x, digits) {
166
+ validateInteger(x);
167
+ const [head, ...digs] = x.split("");
168
+ let borrow = true;
169
+ for (let i = digs.length - 1; borrow && i >= 0; i--) {
170
+ const d = digits.indexOf(digs[i]) - 1;
171
+ if (d === -1) {
172
+ digs[i] = digits.slice(-1);
173
+ } else {
174
+ digs[i] = digits[d];
175
+ borrow = false;
176
+ }
177
+ }
178
+ if (borrow) {
179
+ if (head === "a") {
180
+ return "Z" + digits.slice(-1);
181
+ }
182
+ if (head === "A") {
183
+ return null;
184
+ }
185
+ const h = String.fromCharCode(head.charCodeAt(0) - 1);
186
+ if (h < "Z") {
187
+ digs.push(digits.slice(-1));
188
+ } else {
189
+ digs.pop();
190
+ }
191
+ return h + digs.join("");
192
+ } else {
193
+ return head + digs.join("");
194
+ }
195
+ }
196
+
197
+ // `a` is an order key or null (START).
198
+ // `b` is an order key or null (END).
199
+ // `a < b` lexicographically if both are non-null.
200
+ // digits is a string such as '0123456789' for base 10. Digits must be in
201
+ // ascending character code order!
202
+ /**
203
+ * @param {string | null | undefined} a
204
+ * @param {string | null | undefined} b
205
+ * @param {string=} digits
206
+ * @return {string}
207
+ */
208
+ function generateKeyBetween(a, b, digits = BASE_62_DIGITS) {
209
+ if (a != null) {
210
+ validateOrderKey(a, digits);
211
+ }
212
+ if (b != null) {
213
+ validateOrderKey(b, digits);
214
+ }
215
+ if (a != null && b != null && a >= b) {
216
+ throw new Error(a + " >= " + b);
217
+ }
218
+ if (a == null) {
219
+ if (b == null) {
220
+ return "a" + digits[0];
221
+ }
222
+
223
+ const ib = getIntegerPart(b);
224
+ const fb = b.slice(ib.length);
225
+ if (ib === "A" + digits[0].repeat(26)) {
226
+ return ib + midpoint("", fb, digits);
227
+ }
228
+ if (ib < b) {
229
+ return ib;
230
+ }
231
+ const res = decrementInteger(ib, digits);
232
+ if (res == null) {
233
+ throw new Error("cannot decrement any more");
234
+ }
235
+ return res;
236
+ }
237
+
238
+ if (b == null) {
239
+ const ia = getIntegerPart(a);
240
+ const fa = a.slice(ia.length);
241
+ const i = incrementInteger(ia, digits);
242
+ return i == null ? ia + midpoint(fa, null, digits) : i;
243
+ }
244
+
245
+ const ia = getIntegerPart(a);
246
+ const fa = a.slice(ia.length);
247
+ const ib = getIntegerPart(b);
248
+ const fb = b.slice(ib.length);
249
+ if (ia === ib) {
250
+ return ia + midpoint(fa, fb, digits);
251
+ }
252
+ const i = incrementInteger(ia, digits);
253
+ if (i == null) {
254
+ throw new Error("cannot increment any more");
255
+ }
256
+ if (i < b) {
257
+ return i;
258
+ }
259
+ return ia + midpoint(fa, null, digits);
260
+ }
261
+
262
+ /**
263
+ * same preconditions as generateKeysBetween.
264
+ * n >= 0.
265
+ * Returns an array of n distinct keys in sorted order.
266
+ * If a and b are both null, returns [a0, a1, ...]
267
+ * If one or the other is null, returns consecutive "integer"
268
+ * keys. Otherwise, returns relatively short keys between
269
+ * a and b.
270
+ * @param {string | null | undefined} a
271
+ * @param {string | null | undefined} b
272
+ * @param {number} n
273
+ * @param {string} digits
274
+ * @return {string[]}
275
+ */
276
+ function generateNKeysBetween(a, b, n, digits = BASE_62_DIGITS) {
277
+ if (n === 0) {
278
+ return [];
279
+ }
280
+ if (n === 1) {
281
+ return [generateKeyBetween(a, b, digits)];
282
+ }
283
+ if (b == null) {
284
+ let c = generateKeyBetween(a, b, digits);
285
+ const result = [c];
286
+ for (let i = 0; i < n - 1; i++) {
287
+ c = generateKeyBetween(c, b, digits);
288
+ result.push(c);
289
+ }
290
+ return result;
291
+ }
292
+ if (a == null) {
293
+ let c = generateKeyBetween(a, b, digits);
294
+ const result = [c];
295
+ for (let i = 0; i < n - 1; i++) {
296
+ c = generateKeyBetween(a, c, digits);
297
+ result.push(c);
298
+ }
299
+ result.reverse();
300
+ return result;
301
+ }
302
+ const mid = Math.floor(n / 2);
303
+ const c = generateKeyBetween(a, b, digits);
304
+ return [
305
+ ...generateNKeysBetween(a, c, mid, digits),
306
+ c,
307
+ ...generateNKeysBetween(c, b, n - mid - 1, digits),
308
+ ];
309
+ }
310
+
311
+ module.exports = { BASE_62_DIGITS, generateKeyBetween, generateNKeysBetween };
@@ -0,0 +1,65 @@
1
+ // Minimal, dependency-free image dimension reader. Supports PNG, JPEG, GIF, WebP, BMP.
2
+ // Returns { width, height, mime } or throws.
3
+ const fs = require('fs');
4
+
5
+ function fromBuffer(buf) {
6
+ // PNG: \x89PNG\r\n\x1a\n, IHDR width/height at bytes 16..24 (big-endian)
7
+ if (buf.length >= 24 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) {
8
+ return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20), mime: 'image/png' };
9
+ }
10
+ // GIF: 'GIF87a' / 'GIF89a', width/height little-endian at bytes 6..10
11
+ if (buf.length >= 10 && buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46) {
12
+ return { width: buf.readUInt16LE(6), height: buf.readUInt16LE(8), mime: 'image/gif' };
13
+ }
14
+ // BMP
15
+ if (buf.length >= 26 && buf[0] === 0x42 && buf[1] === 0x4d) {
16
+ return { width: buf.readInt32LE(18), height: Math.abs(buf.readInt32LE(22)), mime: 'image/bmp' };
17
+ }
18
+ // WebP: RIFF....WEBP
19
+ if (buf.length >= 30 && buf.toString('ascii', 0, 4) === 'RIFF' && buf.toString('ascii', 8, 12) === 'WEBP') {
20
+ const fmt = buf.toString('ascii', 12, 16);
21
+ if (fmt === 'VP8 ') {
22
+ return { width: buf.readUInt16LE(26) & 0x3fff, height: buf.readUInt16LE(28) & 0x3fff, mime: 'image/webp' };
23
+ } else if (fmt === 'VP8L') {
24
+ const b = buf.readUInt32LE(21);
25
+ return { width: (b & 0x3fff) + 1, height: ((b >> 14) & 0x3fff) + 1, mime: 'image/webp' };
26
+ } else if (fmt === 'VP8X') {
27
+ const w = 1 + (buf[24] | (buf[25] << 8) | (buf[26] << 16));
28
+ const h = 1 + (buf[27] | (buf[28] << 8) | (buf[29] << 16));
29
+ return { width: w, height: h, mime: 'image/webp' };
30
+ }
31
+ }
32
+ // JPEG: scan SOFn markers
33
+ if (buf.length >= 4 && buf[0] === 0xff && buf[1] === 0xd8) {
34
+ let off = 2;
35
+ while (off < buf.length) {
36
+ if (buf[off] !== 0xff) { off++; continue; }
37
+ const marker = buf[off + 1];
38
+ // SOF0..SOF15 except DHT(c4) DAC(cc) RSTn — these carry frame dims
39
+ if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
40
+ const height = buf.readUInt16BE(off + 5);
41
+ const width = buf.readUInt16BE(off + 7);
42
+ return { width, height, mime: 'image/jpeg' };
43
+ }
44
+ const len = buf.readUInt16BE(off + 2);
45
+ off += 2 + len;
46
+ }
47
+ }
48
+ throw new Error('unsupported or unrecognized image format');
49
+ }
50
+
51
+ function imageSize(filePath) {
52
+ // Read enough bytes for any header (JPEG may need more; read up to 256KB)
53
+ const fd = fs.openSync(filePath, 'r');
54
+ try {
55
+ const stat = fs.fstatSync(fd);
56
+ const n = Math.min(stat.size, 262144);
57
+ const buf = Buffer.alloc(n);
58
+ fs.readSync(fd, buf, 0, n, 0);
59
+ return fromBuffer(buf);
60
+ } finally {
61
+ fs.closeSync(fd);
62
+ }
63
+ }
64
+
65
+ module.exports = { imageSize, fromBuffer };
@@ -0,0 +1,130 @@
1
+ // House style learned from the vault's presentation boards (MemoryOS High Level, Personalization
2
+ // Module, Research). Excalifont (fontFamily 5), solid fills, always-rounded rects, 2px #1e1e1e ink,
3
+ // Excalidraw's native pastel swatches used semantically. These helpers return ElementSpec[] for
4
+ // build_excalidraw.js's buildExcalidraw({ elements }).
5
+
6
+ const INK = '#1e1e1e';
7
+
8
+ // semantic palette: { fill, stroke } — matches the vault's swatches and their meaning
9
+ const PALETTE = {
10
+ green: { fill: '#b2f2bb', stroke: '#2f9e44' }, // benefit / positive / go
11
+ paleGreen: { fill: '#ebfbee', stroke: '#2f9e44' },
12
+ red: { fill: '#ffc9c9', stroke: '#e03131' }, // problem / pain / risk
13
+ blue: { fill: '#a5d8ff', stroke: '#1971c2' }, // system / neutral / flow node
14
+ paleBlue: { fill: '#e7f5ff', stroke: '#1971c2' },
15
+ purple: { fill: '#d0bfff', stroke: '#6741d9' }, // core service / special
16
+ palePurple: { fill: '#f3f0ff', stroke: '#6741d9' },
17
+ yellow: { fill: '#ffec99', stroke: '#f08c00' }, // processing / attention
18
+ mint: { fill: '#96f2d7', stroke: '#0c8599' }, // result / output
19
+ gray: { fill: '#e9ecef', stroke: '#868e96' }, // muted / inactive
20
+ };
21
+ function pal(c) { return PALETTE[c] || { fill: c, stroke: INK }; }
22
+
23
+ let _gc = 0;
24
+ const newGroup = (hint) => `g${++_gc}-${hint || ''}`;
25
+ const lines = (t) => String(t).split('\n').length;
26
+ // word-wrapped line count at a given width — keeps card labels vertically centered when they wrap
27
+ function wrapLineCount(text, fontSize, width) {
28
+ const perLine = Math.max(1, Math.floor(width / (fontSize * 0.52)));
29
+ let total = 0;
30
+ for (const ln of String(text).split('\n')) {
31
+ const words = ln.split(/\s+/).filter(Boolean);
32
+ if (!words.length) { total += 1; continue; }
33
+ let cur = 0, lc = 1;
34
+ for (const w of words) { const add = (cur ? 1 : 0) + w.length; if (cur + add > perLine && cur > 0) { lc++; cur = w.length; } else cur += add; }
35
+ total += lc;
36
+ }
37
+ return total;
38
+ }
39
+
40
+ // rounded card: filled rect + centered label, grouped so they move together
41
+ function card(o) {
42
+ const { x, y, w = 260, h = 92, text = '', color = 'blue', fontSize = 20, stroke = INK, strokeWidth = 2, textColor = INK, roundness = true } = o;
43
+ const group = o.group || newGroup('card');
44
+ const p = pal(color);
45
+ const textH = wrapLineCount(text, fontSize, w) * fontSize * 1.25;
46
+ const ty = y + Math.max(6, (h - textH) / 2);
47
+ return [
48
+ { type: 'rectangle', x, y, width: w, height: h, strokeColor: stroke, backgroundColor: p.fill, fillStyle: 'solid', strokeWidth, roundness, group },
49
+ { type: 'text', x, y: ty, text, fontSize, color: textColor, width: w, align: 'center', fontFamily: 5, group },
50
+ ];
51
+ }
52
+ // small node (flow step) — card with tighter defaults
53
+ function node(o) { return card(Object.assign({ w: 200, h: 64, fontSize: 18 }, o)); }
54
+
55
+ // big plain section header (no box), like "Configuration" / "Flow"
56
+ function sectionTitle(o) {
57
+ const { x, y, text, fontSize = 44, color = INK } = o;
58
+ return [{ type: 'text', x, y, text, fontSize, color, fontFamily: 5, width: Math.max(200, String(text).length * fontSize * 0.6) }];
59
+ }
60
+
61
+ // labeled connector arrow between two points (centers of cards, etc.)
62
+ function connector(o) {
63
+ const { from, to, label, double = false, strokeColor = INK, strokeWidth = 2, dashed = false, labelColor = '#495057', fontSize = 16 } = o;
64
+ const out = [{ type: 'arrow', points: [from, to], strokeColor, strokeWidth, strokeStyle: dashed ? 'dashed' : 'solid', endArrow: 'arrow', startArrow: double ? 'arrow' : null }];
65
+ if (label) {
66
+ const mx = (from[0] + to[0]) / 2, my = (from[1] + to[1]) / 2;
67
+ const w = Math.max(40, String(label).length * fontSize * 0.62);
68
+ out.push({ type: 'text', x: mx - w / 2, y: my - fontSize - 4, text: label, fontSize, color: labelColor, width: w, align: 'center', fontFamily: 5 });
69
+ }
70
+ return out;
71
+ }
72
+
73
+ // side-annotation: short arrow from a node edge to a spec note (the "90sec / deepseek..." pattern)
74
+ function annotate(o) {
75
+ const { from, to, text, fontSize = 16, color = '#495057' } = o;
76
+ return [
77
+ { type: 'arrow', points: [from, to], strokeColor: color, strokeWidth: 1.5, endArrow: 'arrow' },
78
+ { type: 'text', x: to[0] + 6, y: to[1] - fontSize * 0.6, text, fontSize, color, width: Math.max(80, String(text).split('\n')[0].length * fontSize * 0.62), align: 'left', fontFamily: 5 },
79
+ ];
80
+ }
81
+
82
+ // vertical stack of cards (benefit/risk columns)
83
+ function column(o) {
84
+ const { x, y, w = 280, h = 70, gap = 16, fontSize = 18, color = 'green', items = [] } = o;
85
+ const out = [];
86
+ items.forEach((it, i) => {
87
+ const t = typeof it === 'string' ? it : it.text;
88
+ const c = (typeof it === 'object' && it.color) || color;
89
+ out.push(...card({ x, y: y + i * (h + gap), w, h, text: t, color: c, fontSize }));
90
+ });
91
+ return out;
92
+ }
93
+
94
+ // hub-and-spoke: central node + satellites with arrows (the "Personalization Layer" + APIs pattern)
95
+ function hub(o) {
96
+ const { cx, cy, label, color = 'purple', rx = 64, ry = 44, radius = 170, spokes = [], spokeColor = 'blue', spokeRx = 30, spokeRy = 30, startAngle = -Math.PI / 2 } = o;
97
+ const group = newGroup('hub');
98
+ const out = [];
99
+ const p = pal(color);
100
+ out.push({ type: 'ellipse', x: cx - rx, y: cy - ry, width: rx * 2, height: ry * 2, strokeColor: p.stroke, backgroundColor: p.fill, fillStyle: 'solid', strokeWidth: 2 });
101
+ out.push({ type: 'text', x: cx - rx, y: cy - 10, text: label, fontSize: 16, color: INK, width: rx * 2, align: 'center', fontFamily: 5 });
102
+ const n = spokes.length;
103
+ spokes.forEach((s, i) => {
104
+ const a = startAngle + (i / Math.max(1, n)) * Math.PI * 2;
105
+ const sx = cx + Math.cos(a) * radius, sy = cy + Math.sin(a) * radius;
106
+ const c = (typeof s === 'object' && s.color) || spokeColor;
107
+ const t = typeof s === 'string' ? s : s.label;
108
+ const sp = pal(c);
109
+ out.push({ type: 'arrow', points: [[cx + Math.cos(a) * rx, cy + Math.sin(a) * ry], [sx - Math.cos(a) * spokeRx, sy - Math.sin(a) * spokeRy]], strokeColor: INK, strokeWidth: 2, endArrow: 'arrow' });
110
+ out.push({ type: 'ellipse', x: sx - spokeRx, y: sy - spokeRy, width: spokeRx * 2, height: spokeRy * 2, strokeColor: sp.stroke, backgroundColor: sp.fill, fillStyle: 'solid', strokeWidth: 2 });
111
+ out.push({ type: 'text', x: sx - spokeRx, y: sy - 8, text: t, fontSize: 14, color: INK, width: spokeRx * 2, align: 'center', fontFamily: 5 });
112
+ });
113
+ return out;
114
+ }
115
+
116
+ // bullet list as a single left-aligned block (their "Configuration" spec list pattern)
117
+ function bullets(o) {
118
+ const { x, y, items = [], fontSize = 20, color = INK, bullet = '• ', width } = o;
119
+ const text = items.map((i) => bullet + i).join('\n');
120
+ return [{ type: 'text', x, y, text, fontSize, color, fontFamily: 5, width: width || Math.max(200, Math.max(...items.map((i) => i.length)) * fontSize * 0.6) }];
121
+ }
122
+
123
+ // center helpers for wiring connectors to card edges
124
+ const center = (x, y, w, h) => [x + w / 2, y + h / 2];
125
+ const rightOf = (x, y, w, h) => [x + w, y + h / 2];
126
+ const leftOf = (x, y, w, h) => [x, y + h / 2];
127
+ const bottomOf = (x, y, w, h) => [x + w / 2, y + h];
128
+ const topOf = (x, y, w, h) => [x + w / 2, y];
129
+
130
+ module.exports = { INK, PALETTE, pal, card, node, sectionTitle, connector, annotate, column, hub, bullets, center, rightOf, leftOf, bottomOf, topOf };