@usableapp/cardds 0.7.99 → 0.7.100

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/lint/rules.mjs CHANGED
@@ -119,7 +119,8 @@ function* styleEntries(body, at, sep) {
119
119
  export function checkSource(source, file, { classes = carddsClasses() } = {}) {
120
120
  const findings = [];
121
121
  const lines = source.split('\n');
122
- let text = source.replace(/\/\*[\s\S]*?\*\/|(?<![:'"`\\])\/\/[^\n]*/g, (c) => c.replace(/[^\n]/g, ' ')); // comments out, lines kept
122
+ // comments out, lines kept; a string is stepped over whole, so `accept="image/*"` opens no comment
123
+ let text = source.replace(/"[^"\n]*"|'[^'\n]*'|`[^`]*`|\/\*[\s\S]*?\*\/|(?<![:'"`\\])\/\/[^\n]*/g, (c) => /^["'`]/.test(c) ? c : c.replace(/[^\n]/g, ' '));
123
124
  const add = (rule, level, idx, message) => {
124
125
  const line = lineOf(text, idx);
125
126
  if (!/cardds-ignore-next-line/.test(lines[line - 2] ?? '')) findings.push({ rule, level, file, line, message }); // `// cardds-ignore-next-line` — a deliberate case (a story that shows the wrong way)
@@ -229,25 +230,38 @@ export function checkSource(source, file, { classes = carddsClasses() } = {}) {
229
230
 
230
231
  // 10 · the sheet is the paper the member fills in (Lh 2026-10-18): every control the member fills or picks stands on a Sheet's body, a SheetStep
231
232
  // or a stack sheet; a card is for reading and pressing buttons. A Card that is a sheet of a SheetStack IS a sheet (the stack's material)
232
- const span = (re) => [...text.matchAll(re)].map((m) => ({ at: m.index, body: m.index + tagAt(text, m.index).length, end: m.index + elementAt(text, m.index).length }));
233
+ // a `<` right after a name is a type argument (`Record<Step, string>`, `useState<Step>`), not a tag; an element that never closes holds nothing
234
+ const span = (re) => [...text.matchAll(re)].filter((m) => !/[\w$)\].]/.test(text[m.index - 1] ?? '')).map((m) => {
235
+ const open = tagAt(text, m.index), el = elementAt(text, m.index), closed = open.endsWith('/>') || el.endsWith(`</${m[0].slice(1).match(/^[\w.]+/)[0]}>`);
236
+ return { at: m.index, body: m.index + open.length, end: m.index + (closed ? el : open).length };
237
+ });
233
238
  const stacks = span(/<SheetStack\b(?![\w.])/g), cards = span(/<Card\b(?![\w.])/g);
234
239
  const within = (at, el) => at > el.at && at < el.end;
235
240
  const innermost = (at) => cards.filter((c) => within(at, c)).at(-1);
236
241
  // a form is never on the ground (Lh 2026-10-18, timebank's ask steps on a bare Page): the control's nearest container written in this file decides.
237
242
  // None in the file (a step component's fragment) is not judged here — the file that places it is
238
243
  const places = [
239
- ...span(/<(?:Sheet|SheetBody|SheetStep|StackSheet|SheetStack)\b(?![\w.])/g).map((p) => ({ ...p, ground: false })),
244
+ ...span(/<(?:Sheet|SheetBody|SheetStep|StackSheet|SheetStack|CallSheet|Modal|ModalAsk)\b(?![\w.])/g).map((p) => ({ ...p, ground: false })), // a modal is a surface over the screen too
240
245
  ...span(/<(?:ActionBarTier|FilterRow|TopBar|AppBar)\b(?![\w.])/g).map((p) => ({ ...p, ground: false })), // a bar's control: a Slider in a control tier, a Dropdown in a filter row or a top bar's actions, a Toggle in an app bar
241
246
  ...span(/<(?:Page|Pages|Step|Screen)\b(?![\w.])/g).map((p) => ({ ...p, ground: true })),
242
247
  ...cards.map((p) => ({ ...p, ground: false })), // a card round it is field-in-card's finding
243
248
  ].sort((a, b) => a.at - b.at);
244
- const onGround = (at) => places.filter((p) => within(at, p)).at(-1)?.ground;
249
+ // true on a sheet, false on the ground, null when this file cannot tell. A component of this file is where it is placed: the ask steps' <Page>,
250
+ // in a function rendered inside a <SheetBody> further up the file, slides inside that sheet
251
+ const componentAt = (at) => [...text.slice(0, at).matchAll(/(?:function\s+|const\s+)([A-Z]\w*)/g)].at(-1)?.[1];
252
+ const onSheet = (at, depth = 0) => {
253
+ const place = places.filter((p) => within(at, p)).at(-1);
254
+ if (place && !place.ground) return true;
255
+ const name = depth < 5 && componentAt(at);
256
+ if (name) for (const u of text.matchAll(new RegExp(`(?<![\\w$)\\].])<${name}\\b(?![\\w.])`, 'g'))) if (onSheet(u.index, depth + 1)) return true;
257
+ return place ? false : null;
258
+ };
245
259
  for (const c of text.matchAll(/<(Field|Composer|ComposerArea|Pin|Dropdown|Slider|Toggle|Check|ChipPick|Calendar|DayStrip|Day|Picker|Mood|AvatarPick|FileBtn|AddRow|MapPick|Tile)\b(?![\w.])/g)) {
246
260
  const kind = c[1] === 'Tile' ? tagAt(text, c.index).match(/\s(pick|upload)\b(?!\s*=\s*\{\s*false)/)?.[1] : null;
247
261
  if (c[1] === 'Tile' && !kind) continue; // a plain or stat Tile is read, not filled
248
262
  const card = innermost(c.index);
249
263
  if (!card) {
250
- if (onGround(c.index)) add('field-off-sheet', 'error', c.index, `<${kind ? `Tile ${kind}` : c[1]}> on the ground: a form is never on the ground, it sits only on a sheet (Lh 2026-10-18). A Page, Pages, Step or Screen is the base, not a sheet. Put the control on a Sheet's <SheetBody>, in a <SheetStep>, or on a stack sheet — a form over a screen is a <Sheet> in a <SheetStage over dim> (Home/AskSheet).`);
264
+ if (onSheet(c.index) === false) add('field-off-sheet', 'error', c.index, `<${kind ? `Tile ${kind}` : c[1]}> on the ground: a form is never on the ground, it sits only on a sheet (Lh 2026-10-18). A Page, Pages, Step or Screen is the base, not a sheet. Put the control on a Sheet's <SheetBody>, in a <SheetStep>, or on a stack sheet — a form over a screen is a <Sheet> in a <SheetStage over dim> (Home/AskSheet).`);
251
265
  continue;
252
266
  }
253
267
  const stack = stacks.filter((s) => card.at >= s.body && card.at < s.end).at(-1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usableapp/cardds",
3
- "version": "0.7.99",
3
+ "version": "0.7.100",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "description": "card-first mobile design system, React-first: the components in src/ are thin wrappers over the CSS contract (css/*.css stays the only truth); gallery/ shows every story live (npm run dev), tests/ measures the geometry.",