synthesisui 0.16.102 → 0.16.103

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.
@@ -1645,8 +1645,21 @@ function rootPackage(tag, sketch) {
1645
1645
  : {};
1646
1646
  }
1647
1647
  async function resolveReadParts(census, root) {
1648
- const read = census.reading?.components;
1649
- if (!read)
1648
+ /**
1649
+ * A READING IS OPTIONAL HERE. THE SKETCH IS NOT.
1650
+ *
1651
+ * This returned on line one when nobody had authored a reading, and a DERIVED
1652
+ * anatomy needs no reading at all - so the deriver shipped and never ran on the
1653
+ * path the owner actually uses. Measured on his own re-import (02/08): the census
1654
+ * carried `rootFrom` on 13 looks and `from` on 162 sketch nodes - the new fields
1655
+ * arrived - and `tree` on 0 of 36. Two components then read as empty contracts
1656
+ * and came back below the floor.
1657
+ *
1658
+ * "Medido 100%, convertido 0%", one week after the law that names it. The guard
1659
+ * is now about having something to convert, not about who asked.
1660
+ */
1661
+ const read = census.reading?.components ?? {};
1662
+ if (!census.looks || Object.keys(census.looks).length === 0)
1650
1663
  return;
1651
1664
  const declared = new Map(Object.entries(census.declared));
1652
1665
  // The manifest is read ONCE for the whole run: the version belongs in a rule's
@@ -1774,10 +1787,14 @@ async function resolveReadParts(census, root) {
1774
1787
  for (const n of resolved.notes)
1775
1788
  notes.add(n);
1776
1789
  }
1777
- const total = Object.keys(read).length;
1790
+ const authored = Object.keys(read).length;
1791
+ const total = authored || Object.keys(census.looks ?? {}).length;
1778
1792
  if (named > 0 || shaped > 0) {
1779
1793
  console.log("");
1780
- console.log(body(`${named} part${named === 1 ? "" : "s"} your reading named across ${total} component${total === 1 ? "" : "s"} - each previews as what it IS rather than as a text box`));
1794
+ console.log(body(
1795
+ // "Your reading" only when there WAS one - otherwise the names came off
1796
+ // their own markup, and claiming otherwise credits somebody's absent work.
1797
+ `${named} part${named === 1 ? "" : "s"} named across ${total} component${total === 1 ? "" : "s"}${authored > 0 ? " - your reading and their markup" : " - read off their own markup"}, so each previews as what it IS rather than as a text box`));
1781
1798
  }
1782
1799
  if (shaped > 0) {
1783
1800
  console.log(body(`${shaped} of them arrived with a SHAPE - the parts nested as they nest in your code, so the preview is the component and not a row of labels`));
@@ -1889,6 +1906,20 @@ export async function runImport(opts) {
1889
1906
  if (scope)
1890
1907
  census.scope = scope;
1891
1908
  }
1909
+ /**
1910
+ * DERIVE THE ANATOMY ON THE MEASURING PATH TOO.
1911
+ *
1912
+ * The send path resolves the reading above; a plain `--dry` measure never
1913
+ * reached that function, so the census on disk - the file the skill reads to
1914
+ * author its reading, and the file every one of our real-repo specs reads -
1915
+ * carried a sketch for every component and a tree for none. The person's own
1916
+ * re-import proved it: 36 sketches, 162 stamped nodes, 0 trees (dono, 02/08).
1917
+ *
1918
+ * Written into the census means the skill sees the shape it would otherwise
1919
+ * have to author from scratch, and can spend its judgement on the names.
1920
+ */
1921
+ if (!opts.census)
1922
+ await resolveReadParts(census, root);
1892
1923
  summarize(census);
1893
1924
  // Only when nobody has scoped yet. Telling someone to scope to the folder
1894
1925
  // they just scoped to reads as the tool not having noticed.
@@ -23,6 +23,50 @@ import { importMap, packageOfTag } from "./imports.js";
23
23
  import { definitionSpan } from "./transcribe.js";
24
24
  /** `{children}` / `{props.children}` - the caller's content, spelled either way. */
25
25
  const CHILDREN = /\{\s*(?:props\.)?children\s*\}/;
26
+ /**
27
+ * THE TEXT THAT SITS DIRECTLY INSIDE ONE ELEMENT - its own gaps, not its
28
+ * children's contents.
29
+ *
30
+ * The first version of the slot check read only the span between an element's
31
+ * open tag and the next tag of any kind, so it saw `{children}` only when it was
32
+ * the FIRST thing inside. A real panel renders a header and THEN the caller's
33
+ * content, which is the mixed case the reader has a form for - and it was
34
+ * invisible. Found by driving `runImport` over a Base-UI dialog whose `{children}`
35
+ * follows its close button (02/08).
36
+ *
37
+ * Attribution matters as much as detection: `{children}` inside a CHILD is that
38
+ * child's slot, so the walk collects the gaps at this element's own level and
39
+ * skips whole subtrees.
40
+ */
41
+ function directText(source, events, index) {
42
+ const open = events[index];
43
+ const openEnd = source.indexOf(">", open.at);
44
+ if (openEnd === -1)
45
+ return "";
46
+ let from = openEnd + 1;
47
+ const gaps = [];
48
+ let rel = 0;
49
+ for (let j = index + 1; j < events.length; j++) {
50
+ const event = events[j];
51
+ if (event.kind === "open") {
52
+ if (rel === 0)
53
+ gaps.push(source.slice(from, event.at));
54
+ rel += 1;
55
+ continue;
56
+ }
57
+ rel -= 1;
58
+ // Our own close: the last gap runs up to it, and the element ends here.
59
+ if (rel < 0) {
60
+ gaps.push(source.slice(from, event.at));
61
+ break;
62
+ }
63
+ if (rel === 0) {
64
+ const closeEnd = source.indexOf(">", event.at);
65
+ from = closeEnd === -1 ? event.at : closeEnd + 1;
66
+ }
67
+ }
68
+ return gaps.join(" ");
69
+ }
26
70
  /**
27
71
  * Past this a component is a page in disguise, and the gate already said no.
28
72
  *
@@ -177,10 +221,12 @@ export function sketchOf(source, name) {
177
221
  !between.includes("<")) {
178
222
  node.text = between;
179
223
  }
180
- // The caller's content, as a fact - see `SketchNode.slot`.
181
- if (CHILDREN.test(between))
182
- node.slot = true;
183
224
  }
225
+ // The caller's content, as a fact - see `SketchNode.slot`. Read from the
226
+ // element's DIRECT text, so a `{children}` after a header still counts and one
227
+ // inside a child belongs to that child.
228
+ if (CHILDREN.test(directText(source, events, i)))
229
+ node.slot = true;
184
230
  out.push(node);
185
231
  depth += 1;
186
232
  }
@@ -148,11 +148,11 @@ file to pick between. Nothing is sent by \`--dry\`. Read the file.
148
148
  ### 2. Walk them through the decisions - ALL of them, before any reading
149
149
 
150
150
  **Every question this skill asks is answerable from the census you just took.** Source, name,
151
- primary, theme, canvas, our-kit-or-theirs, architecture - the numbers behind all seven are in
152
- the file on their disk, and none of them needs the anatomy.
151
+ primary, theme, canvas, architecture - the numbers behind all six are in the file on their disk,
152
+ and none of them needs the anatomy.
153
153
 
154
154
  They used to be asked AFTER the reading, and that put six hundred lines of work between the
155
- person's first answer and their second (dono, 01/08). A person who answers seven questions in
155
+ person's first answer and their second (dono, 01/08). A person who answers six questions in
156
156
  ninety seconds and then watches a bar has had a good four minutes; the same person interrupted
157
157
  twice in the middle of a silence has had a bad twenty.
158
158
 
@@ -160,7 +160,7 @@ So: ask everything here, in one pass, and **do not come back**. If something you
160
160
  the reading contradicts an answer, you do not reopen the question - you say so in the report at
161
161
  the end and let them change it there.
162
162
 
163
- Seven decisions are theirs. **Ask them as separate questions with selectable options** - use your
163
+ Six decisions are theirs. **Ask them as separate questions with selectable options** - use your
164
164
  question tool, one call per decision, so they pick instead of reading a wall and composing a
165
165
  reply. A single block containing everything is a report, and a report gets read, not answered.
166
166
 
@@ -244,35 +244,37 @@ canvas #222326 darkgray-500 - what DashboardLayout paints on <main>
244
244
  Say it and let them object. Offering five near-blacks to pick between is asking someone to
245
245
  re-decide something their code already decided.
246
246
 
247
- **2f. Our kit, or theirs alone** - ask this ONLY when they already have a component library,
248
- which is when \`--scope\` pointed at one and the CLI said so. An app with no components of its own
249
- has nothing to weigh and should never see the question.
247
+ **2f. Our kit** - do not ask. A project that already has a component library gets **theirs
248
+ alone**, and you say so in one line:
250
249
 
251
250
  \`\`\`
252
- Every system born here comes with 45 components of ours - button, card, input,
253
- table, the rest of a working toolkit. You already have 37.
254
-
255
- add ours to yours (recommended) 68 components, and the 23 governed by your
256
- own rules are the 23 you wrote
257
- mine alone 37 components, all of them yours. Our
258
- layouts that need a component you do not
259
- have stay out too. Add any of ours later
260
- from the library, one at a time
251
+ This system is yours alone - the 37 components you wrote, and none of ours.
252
+ Add any of ours later from the library, one at a time.
261
253
  \`\`\`
262
254
 
263
- Send it as \`"standards": "none"\` in the reading when they choose the second, and leave the field
264
- out when they choose the first. **Recommend adding ours**, and say why in one line: a component of
265
- ours costs nothing until something composes it, and having a \`table\` the day somebody needs a
266
- table is worth more than a shorter list.
255
+ Send it as \`"standards": "none"\` in the reading. Every time, with one exception below.
256
+
257
+ **Why this stopped being a question.** Somebody who points us at their own library is telling us
258
+ what their system is, and 45 components of ours landing beside 37 of theirs makes a system of 82
259
+ where only 37 are governed by anything they wrote - the number on their own screen stops being
260
+ about them. The kit is one click away in the library afterwards, so choosing wrong here costs a
261
+ click and choosing to ask costs a pause in the middle of their import.
262
+
263
+ **The exception, and it is not a preference.** An app with NO components of its own - \`--scope\`
264
+ pointed at an app rather than a library, and the CLI reported no component library - must NOT get
265
+ \`"standards": "none"\`: theirs alone plus nothing of theirs is a system with zero components. Leave
266
+ the field out there, exactly as before.
267
267
 
268
268
  Two things to be straight about, because both are true and neither is obvious:
269
269
 
270
- - **the foundations come from the seed either way.** Their palette, their scales, their type - all
270
+ - **the foundations still come from the seed.** Their palette, their scales, their type - all
271
271
  theirs - but the seven type slots, the easing and the neutral floor arrive from ours, because
272
272
  the alternative to a type scale is no components at all rather than their components.
273
273
  - **our layouts are written in our recipes.** A landing that composes \`button\`, \`card\` and
274
- \`badge\` needs those to exist. Choosing *mine alone* keeps the layouts whose recipes they
275
- happen to have and drops the rest - a layout pointing at a recipe nobody has renders as holes.
274
+ \`badge\` needs those to exist. Theirs alone keeps the layouts whose recipes they happen to have
275
+ and drops the rest - a layout pointing at a recipe nobody has renders as holes. Say that number
276
+ when the CLI reports it, so a shorter list of layouts is a fact they were told rather than one
277
+ they discover.
276
278
 
277
279
  **2g. Which architecture has priority** - ask only when the CLI reported more than
278
280
  one, which it does under "How this project is organised".
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.102",
3
+ "version": "0.16.103",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {