@codepress/design-kit 0.11.0 → 0.12.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/README.md +27 -5
- package/package.json +1 -1
- package/src/kit.d.ts +8 -0
- package/src/kit.mjs +218 -35
- package/src/templates/design-kit-skill.md +1 -1
package/README.md
CHANGED
|
@@ -521,23 +521,45 @@ keeps the kit's totals and adds `page_sources.<source>` with each one's own.
|
|
|
521
521
|
The tool needs only Node: no install and no network.
|
|
522
522
|
|
|
523
523
|
```bash
|
|
524
|
-
node kit.mjs info
|
|
525
|
-
node kit.mjs find button # ≤ 30 lines: id
|
|
524
|
+
node kit.mjs info [--json] # one line: sha, build time, counts (--json: the manifest)
|
|
525
|
+
node kit.mjs find button # ≤ 30 lines: id (widths grouped), size, title
|
|
526
526
|
node kit.mjs find "transfer app" # the words on screen work too
|
|
527
527
|
node kit.mjs show <id> [--full] # path, size and an outline (≤ 2 KB) or the markup
|
|
528
528
|
node kit.mjs compose --frame route:dashboard@1280 --slot main=new.html \
|
|
529
529
|
[--theme light|dark] [--show-new] [--css pruned|full] --out page.html
|
|
530
530
|
```
|
|
531
531
|
|
|
532
|
-
`
|
|
532
|
+
`info` prints one line, such as
|
|
533
|
+
`480e11d built 2026-11-15T18:58:59.954Z · stories 12821 · routes 133 · failed 13`,
|
|
534
|
+
and names the failed ids from `gaps.json` when there are five or fewer;
|
|
535
|
+
`info --json` prints `manifest.json` as it is.
|
|
536
|
+
|
|
537
|
+
`find` prints one line per frame: rows that differ only by `@<width>` share
|
|
538
|
+
it, with the widths in braces and one size per width (or one size when they
|
|
539
|
+
print the same), for example
|
|
540
|
+
`route:orchestrator@{1280,390} 131/136 KB Orchestrator project`. A frame
|
|
541
|
+
captured at one width prints its plain id. The title is left out when it only
|
|
542
|
+
restates the id (`Chat/SubAgentBlock/Orchestrator Row` for
|
|
543
|
+
`story:chat-subagentblock--orchestrator-row`), and a route's trailing
|
|
544
|
+
` (1280)` is dropped; a frame whose widths have different titles prints each,
|
|
545
|
+
separated by `|`. `find` lists the rows holding every word of the query in their id, title,
|
|
533
546
|
component, source file or visible text. Rows with the words in the id come
|
|
534
547
|
first, then the title, component or source file, then the text only; among
|
|
535
548
|
rows that tie, one whose text holds the whole query comes first. A row found
|
|
536
549
|
only through its text ends with a quoted excerpt of about 60 characters
|
|
537
550
|
around the words, so you see why it matched. When more rows match than the
|
|
538
551
|
30 lines hold, rows found by name fill the list but its last 5 lines are kept
|
|
539
|
-
for rows found by text, and the closing line counts
|
|
540
|
-
(`+37 more by name, +3 more by on-screen text; narrow the query`). The
|
|
552
|
+
for rows found by text, and the closing line counts the rows each left out
|
|
553
|
+
(`+37 more by name, +3 more by on-screen text; narrow the query`). The
|
|
554
|
+
formatter is exported as `findMatchLines(matches)` from
|
|
555
|
+
`@codepress/design-kit/kit`, so the cloud Orchestrator's `design_kit_find`
|
|
556
|
+
prints the same lines.
|
|
557
|
+
|
|
558
|
+
`show` prints the row's header (id, kind and size, title, file, source,
|
|
559
|
+
slots) and an outline of at most 2 KB: one line per element down to depth 4,
|
|
560
|
+
a run of identical siblings (each with what is nested under it) printed once
|
|
561
|
+
with `×N`, and generated ids (`radix-…`, React's `_r…_` and the capture's
|
|
562
|
+
renumbered `_k…_`) left out. `show --full` prints the markup. The text
|
|
541
563
|
is `text.json`, built
|
|
542
564
|
whenever a capture writes the kit: each row's markup without its scripts,
|
|
543
565
|
styles, SVG, head and templates, tags and attribute values dropped, entities
|
package/package.json
CHANGED
package/src/kit.d.ts
CHANGED
|
@@ -51,6 +51,14 @@ export function findMatches(kit: Kit, query: string): KitMatch[];
|
|
|
51
51
|
/** The rows of `findMatches`, in the same order. */
|
|
52
52
|
export function find(kit: Kit, query: string): KitRow[];
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* `kit find`'s lines for `findMatches` results, at most 30: rows that differ
|
|
56
|
+
* only by width share one line (`route:x@{1280,390} 131/136 KB Title`), a
|
|
57
|
+
* title that only restates the id is left out, and when the matches do not
|
|
58
|
+
* fit, a last line counts the rows not shown.
|
|
59
|
+
*/
|
|
60
|
+
export function findMatchLines(matches: KitMatch[]): string[];
|
|
61
|
+
|
|
54
62
|
/** Characters of visible text text.json keeps per row: 600. */
|
|
55
63
|
export const TEXT_LIMIT: number;
|
|
56
64
|
|
package/src/kit.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* The design kit's own tool. It ships inside every kit, so tool and data
|
|
4
4
|
* always match. Pure Node: no dependencies, no network.
|
|
5
5
|
*
|
|
6
|
-
* node kit.mjs info
|
|
6
|
+
* node kit.mjs info [--json]
|
|
7
7
|
* node kit.mjs find <query> (ids, titles and the words on screen)
|
|
8
8
|
* node kit.mjs show <id> [--full]
|
|
9
9
|
* node kit.mjs compose --frame <id> [--slot <name>=<file>]… [--theme light|dark]
|
|
@@ -1834,10 +1834,16 @@ function excerpt(words, at, length) {
|
|
|
1834
1834
|
|
|
1835
1835
|
// ── show / find / info ──────────────────────────────────────────────────────
|
|
1836
1836
|
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1837
|
+
/**
|
|
1838
|
+
* An id attribute a library or React generated (`radix-…`, React's `useId`
|
|
1839
|
+
* values as the capture renumbers them): it changes between builds and says
|
|
1840
|
+
* nothing about the element, so the outline leaves it out.
|
|
1841
|
+
*/
|
|
1842
|
+
const GENERATED_ID = /^radix-|_r_?[0-9a-z]+_|_k\d+_|:r[0-9a-z]+:/;
|
|
1843
|
+
|
|
1844
|
+
/** One outline line per element down to depth 4, with its depth. */
|
|
1845
|
+
function outlineEntries(html) {
|
|
1846
|
+
const entries = [];
|
|
1841
1847
|
for (const element of elements(html)) {
|
|
1842
1848
|
if (element.depth > 4) continue;
|
|
1843
1849
|
const classes = (element.attrs.class ?? "")
|
|
@@ -1848,10 +1854,71 @@ export function outline(html, limit = SHOW_LIMIT) {
|
|
|
1848
1854
|
.join("");
|
|
1849
1855
|
const marks = ["id", "role", "data-kit-slot", "aria-label", "data-testid"]
|
|
1850
1856
|
.filter((name) => element.attrs[name])
|
|
1857
|
+
.filter((name) => name !== "id" || !GENERATED_ID.test(element.attrs.id))
|
|
1851
1858
|
.map((name) => `[${name}=${element.attrs[name].slice(0, 40)}]`)
|
|
1852
1859
|
.join("");
|
|
1853
1860
|
const text = element.text.replace(/\s+/g, " ").trim().slice(0, 40);
|
|
1854
|
-
|
|
1861
|
+
entries.push({
|
|
1862
|
+
depth: element.depth,
|
|
1863
|
+
line: `${" ".repeat(element.depth)}<${element.name}${classes}${marks}>${text ? ` "${text}"` : ""}`,
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
return entries;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
/**
|
|
1870
|
+
* Runs of identical siblings, each with the lines nested under it, printed
|
|
1871
|
+
* once with `×N` after the first line.
|
|
1872
|
+
*/
|
|
1873
|
+
function collapseRuns(entries) {
|
|
1874
|
+
// ends[i]: the index after the last line nested under line i.
|
|
1875
|
+
const ends = new Array(entries.length).fill(entries.length);
|
|
1876
|
+
const open = [];
|
|
1877
|
+
entries.forEach((entry, i) => {
|
|
1878
|
+
while (open.length && entries[open.at(-1)].depth >= entry.depth)
|
|
1879
|
+
ends[open.pop()] = i;
|
|
1880
|
+
open.push(i);
|
|
1881
|
+
});
|
|
1882
|
+
const same = (a, b, length) => {
|
|
1883
|
+
for (let k = 0; k < length; k += 1)
|
|
1884
|
+
if (entries[a + k].line !== entries[b + k].line) return false;
|
|
1885
|
+
return true;
|
|
1886
|
+
};
|
|
1887
|
+
const lines = [];
|
|
1888
|
+
const walk = (from, to) => {
|
|
1889
|
+
let i = from;
|
|
1890
|
+
while (i < to) {
|
|
1891
|
+
const length = ends[i] - i;
|
|
1892
|
+
let next = ends[i];
|
|
1893
|
+
let count = 1;
|
|
1894
|
+
while (
|
|
1895
|
+
next < to &&
|
|
1896
|
+
entries[next].depth === entries[i].depth &&
|
|
1897
|
+
ends[next] - next === length &&
|
|
1898
|
+
same(i, next, length)
|
|
1899
|
+
) {
|
|
1900
|
+
count += 1;
|
|
1901
|
+
next = ends[next];
|
|
1902
|
+
}
|
|
1903
|
+
lines.push(`${entries[i].line}${count > 1 ? ` ×${count}` : ""}`);
|
|
1904
|
+
walk(i + 1, ends[i]);
|
|
1905
|
+
i = next;
|
|
1906
|
+
}
|
|
1907
|
+
};
|
|
1908
|
+
walk(0, entries.length);
|
|
1909
|
+
return lines;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
/**
|
|
1913
|
+
* A markup outline of at most `limit` bytes: one line per element down to
|
|
1914
|
+
* depth 4, identical sibling runs collapsed to one line with `×N`, and
|
|
1915
|
+
* generated ids left out.
|
|
1916
|
+
*/
|
|
1917
|
+
export function outline(html, limit = SHOW_LIMIT) {
|
|
1918
|
+
const lines = [];
|
|
1919
|
+
let size = 0;
|
|
1920
|
+
let truncated = false;
|
|
1921
|
+
for (const line of collapseRuns(outlineEntries(html))) {
|
|
1855
1922
|
// The limit is bytes: an outline line can carry non-ASCII text.
|
|
1856
1923
|
const bytes = Buffer.byteLength(line) + 1;
|
|
1857
1924
|
if (size + bytes > limit) {
|
|
@@ -1936,32 +2003,103 @@ export function find(kit, query) {
|
|
|
1936
2003
|
return findMatches(kit, query).map(({ row }) => row);
|
|
1937
2004
|
}
|
|
1938
2005
|
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
2006
|
+
/** Letters and digits only, lowercased: how a title can restate an id. */
|
|
2007
|
+
function flat(text) {
|
|
2008
|
+
return text.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
/** `story:x@1280` → `story:x`: the id without its width. */
|
|
2012
|
+
function baseId(id) {
|
|
2013
|
+
return id.replace(/@\d+$/, "");
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/** The width an id ends in, as written. */
|
|
2017
|
+
function idWidth(id) {
|
|
2018
|
+
return /@(\d+)$/.exec(id)?.[1] ?? null;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
/** Sizes of a width group: `131/136 KB`, `443 B/1.2 KB`, or `4.9 KB` when they print the same. */
|
|
2022
|
+
function groupBytes(rows) {
|
|
2023
|
+
const sizes = rows.map((row) => row.bytes ?? 0);
|
|
2024
|
+
const kb = (bytes) =>
|
|
2025
|
+
bytes >= 10 * 1024
|
|
2026
|
+
? `${Math.round(bytes / 1024)}`
|
|
2027
|
+
: `${(bytes / 1024).toFixed(1)}`;
|
|
2028
|
+
// Widths that print the same size print it once.
|
|
2029
|
+
const once = (values) => [...new Set(values)].join("/");
|
|
2030
|
+
if (sizes.every((bytes) => bytes >= 1024)) return `${once(sizes.map(kb))} KB`;
|
|
2031
|
+
return once(
|
|
2032
|
+
sizes.map((bytes) => (bytes >= 1024 ? `${kb(bytes)} KB` : `${bytes} B`)),
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
/**
|
|
2037
|
+
* Matches gathered into lines: rows that differ only by `@<width>` and found
|
|
2038
|
+
* the same way (the same excerpt, or none) share one line, at the place of
|
|
2039
|
+
* the first of them.
|
|
2040
|
+
*/
|
|
2041
|
+
function widthGroups(matches) {
|
|
2042
|
+
const groups = new Map();
|
|
2043
|
+
for (const match of matches) {
|
|
2044
|
+
// An id without a width never shares a line.
|
|
2045
|
+
const id = match.row.id;
|
|
2046
|
+
const key = `${idWidth(id) ? baseId(id) : `${id}\0`}\0${match.excerpt ?? ""}`;
|
|
2047
|
+
const group = groups.get(key);
|
|
2048
|
+
if (group) group.rows.push(match.row);
|
|
2049
|
+
else groups.set(key, { rows: [match.row], excerpt: match.excerpt });
|
|
1949
2050
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2051
|
+
return [...groups.values()];
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
/** One find line: id (with its widths), sizes, title unless it restates the id, excerpt. */
|
|
2055
|
+
function groupLine({ rows, excerpt: cut }) {
|
|
2056
|
+
const base = baseId(rows[0].id);
|
|
2057
|
+
const widths = rows.map((row) => idWidth(row.id));
|
|
2058
|
+
const id = rows.length === 1 ? rows[0].id : `${base}@{${widths.join(",")}}`;
|
|
2059
|
+
const name = flat(base.replace(/^[a-z]+:/, ""));
|
|
2060
|
+
const titles = [];
|
|
2061
|
+
for (const row of rows) {
|
|
2062
|
+
const title = (row.title ?? "").replace(/\s*\(\d+\)$/, "").trim();
|
|
2063
|
+
if (title && flat(title) !== name && !titles.includes(title))
|
|
2064
|
+
titles.push(title);
|
|
2065
|
+
}
|
|
2066
|
+
return [
|
|
2067
|
+
id,
|
|
2068
|
+
groupBytes(rows),
|
|
2069
|
+
...(titles.length ? [titles.join(" | ")] : []),
|
|
2070
|
+
...(cut === undefined ? [] : [`"${cut}"`]),
|
|
2071
|
+
].join(" ");
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
/**
|
|
2075
|
+
* `kit find`'s lines for `findMatches` results, at most FIND_LIMIT: rows that
|
|
2076
|
+
* differ only by width share a line (`route:x@{1280,390}`), a title that only
|
|
2077
|
+
* restates the id is left out, and a last line counts the rows not shown.
|
|
2078
|
+
* Rows found by name come first, but a few lines are kept for rows found only
|
|
2079
|
+
* by their on-screen text.
|
|
2080
|
+
*/
|
|
2081
|
+
export function findMatchLines(matches) {
|
|
2082
|
+
const count = (groups) =>
|
|
2083
|
+
groups.reduce((sum, group) => sum + group.rows.length, 0);
|
|
1954
2084
|
// A row with an excerpt was found by on-screen text; any other, by name.
|
|
1955
|
-
const
|
|
2085
|
+
const byName = widthGroups(
|
|
2086
|
+
matches.filter((match) => match.excerpt === undefined),
|
|
2087
|
+
);
|
|
2088
|
+
const byText = widthGroups(
|
|
2089
|
+
matches.filter((match) => match.excerpt !== undefined),
|
|
2090
|
+
);
|
|
2091
|
+
// Keep the order findMatches ranked them in when everything fits.
|
|
2092
|
+
if (byName.length + byText.length <= FIND_LIMIT)
|
|
2093
|
+
return widthGroups(matches).map(groupLine);
|
|
2094
|
+
const room = FIND_LIMIT - 1;
|
|
1956
2095
|
if (!byText.length) {
|
|
1957
2096
|
return [
|
|
1958
|
-
...
|
|
1959
|
-
`… ${
|
|
2097
|
+
...byName.slice(0, room).map(groupLine),
|
|
2098
|
+
`… ${count(byName.slice(room))} more; add a word to narrow it`,
|
|
1960
2099
|
];
|
|
1961
2100
|
}
|
|
1962
2101
|
// Names fill the list first, but the last lines are kept for text hits,
|
|
1963
2102
|
// so an agent sees them even when names alone would fill it.
|
|
1964
|
-
const byName = matches.filter((match) => match.excerpt === undefined);
|
|
1965
2103
|
const textRoom = Math.min(
|
|
1966
2104
|
byText.length,
|
|
1967
2105
|
Math.max(FIND_TEXT_LINES, room - byName.length),
|
|
@@ -1970,19 +2108,33 @@ function findLines(kit, query) {
|
|
|
1970
2108
|
const texts = byText.slice(0, room - names.length);
|
|
1971
2109
|
const left = [
|
|
1972
2110
|
...(byName.length > names.length
|
|
1973
|
-
? [`+${byName.
|
|
2111
|
+
? [`+${count(byName.slice(names.length))} more by name`]
|
|
1974
2112
|
: []),
|
|
1975
2113
|
...(byText.length > texts.length
|
|
1976
|
-
? [`+${byText.
|
|
2114
|
+
? [`+${count(byText.slice(texts.length))} more by on-screen text`]
|
|
1977
2115
|
: []),
|
|
1978
2116
|
];
|
|
1979
2117
|
return [
|
|
1980
|
-
...names.map(
|
|
1981
|
-
...texts.map(
|
|
2118
|
+
...names.map(groupLine),
|
|
2119
|
+
...texts.map(groupLine),
|
|
1982
2120
|
`${left.join(", ")}; narrow the query`,
|
|
1983
2121
|
];
|
|
1984
2122
|
}
|
|
1985
2123
|
|
|
2124
|
+
function findLines(kit, query) {
|
|
2125
|
+
const matches = findMatches(kit, query);
|
|
2126
|
+
if (!matches.length) {
|
|
2127
|
+
const near = nearMatches(
|
|
2128
|
+
query,
|
|
2129
|
+
kit.rows.map((row) => row.id),
|
|
2130
|
+
);
|
|
2131
|
+
throw new KitError(
|
|
2132
|
+
`nothing matches "${query}".${near.length ? ` Near: ${near.join(", ")}` : ""}`,
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
return findMatchLines(matches);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
1986
2138
|
function showText(kit, id, full) {
|
|
1987
2139
|
const row = requireRow(kit, id, "id");
|
|
1988
2140
|
const html = readSnippet(kit, row);
|
|
@@ -2003,19 +2155,49 @@ function showText(kit, id, full) {
|
|
|
2003
2155
|
return [head, ...lines, ...(truncated ? [tail] : [])].join("\n");
|
|
2004
2156
|
}
|
|
2005
2157
|
|
|
2006
|
-
|
|
2158
|
+
/** gaps.json's ids: the frames a capture failed on. */
|
|
2159
|
+
function failedIds(dir) {
|
|
2160
|
+
try {
|
|
2161
|
+
const gaps = JSON.parse(readFileSync(join(dir, "gaps.json"), "utf8"));
|
|
2162
|
+
if (!Array.isArray(gaps)) return [];
|
|
2163
|
+
return gaps.map((gap) => gap?.id).filter((id) => typeof id === "string");
|
|
2164
|
+
} catch {
|
|
2165
|
+
// Optional: info prints the count without the ids.
|
|
2166
|
+
return [];
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
/**
|
|
2171
|
+
* One line: `sha7 built <iso> · stories N · routes N · failed N`, with the
|
|
2172
|
+
* failed ids when there are five or fewer. `json` prints manifest.json.
|
|
2173
|
+
*/
|
|
2174
|
+
function infoText(kit, json) {
|
|
2175
|
+
const manifest = kit.manifest ?? { missing: "manifest.json" };
|
|
2176
|
+
if (json) return JSON.stringify(manifest, null, 2);
|
|
2007
2177
|
const routes = kit.rows.filter((row) => rowSource(row) === "routes").length;
|
|
2178
|
+
const sha = typeof manifest.sha === "string" ? manifest.sha.slice(0, 7) : "";
|
|
2179
|
+
const built =
|
|
2180
|
+
typeof manifest.built_at === "string" ? `built ${manifest.built_at}` : "";
|
|
2181
|
+
const ids = failedIds(kit.dir);
|
|
2182
|
+
const counted = manifest.counts?.failed;
|
|
2183
|
+
const failed = typeof counted === "number" ? counted : ids.length;
|
|
2184
|
+
const head = kit.manifest
|
|
2185
|
+
? [sha, built].filter(Boolean).join(" ") || "manifest.json"
|
|
2186
|
+
: "no manifest.json";
|
|
2008
2187
|
return [
|
|
2009
|
-
|
|
2010
|
-
`
|
|
2011
|
-
|
|
2188
|
+
head,
|
|
2189
|
+
`stories ${kit.rows.length - routes}`,
|
|
2190
|
+
`routes ${routes}`,
|
|
2191
|
+
`failed ${failed}${failed > 0 && failed <= 5 && ids.length ? ` (${ids.join(", ")})` : ""}`,
|
|
2192
|
+
].join(" · ");
|
|
2012
2193
|
}
|
|
2013
2194
|
|
|
2014
2195
|
// ── CLI ─────────────────────────────────────────────────────────────────────
|
|
2015
2196
|
|
|
2016
2197
|
const USAGE = `usage: kit.mjs [--kit <dir>] <command>
|
|
2017
|
-
info
|
|
2018
|
-
find <query>
|
|
2198
|
+
info [--json] sha, build time and counts (--json: the manifest)
|
|
2199
|
+
find <query> up to ${FIND_LIMIT} lines of rows whose id, title or on-screen text holds
|
|
2200
|
+
every word; widths of one frame share a line: id@{1280,390}
|
|
2019
2201
|
show <id> [--full] path, size and an outline (--full: the markup)
|
|
2020
2202
|
compose --frame <id> [--slot <name>=<file>]… [--theme light|dark] [--show-new] [--css pruned|full] --out <file>
|
|
2021
2203
|
--css full keeps every rule of the kit's CSS; the default
|
|
@@ -2038,6 +2220,7 @@ function parseArgs(argv) {
|
|
|
2038
2220
|
else if (arg === "--out") options.out = value();
|
|
2039
2221
|
else if (arg === "--css") options.css = value();
|
|
2040
2222
|
else if (arg === "--full") options.full = true;
|
|
2223
|
+
else if (arg === "--json") options.json = true;
|
|
2041
2224
|
else if (arg === "--show-new") options.showNew = true;
|
|
2042
2225
|
else if (arg === "--help" || arg === "-h") options.help = true;
|
|
2043
2226
|
else if (arg.startsWith("--")) throw new KitError(`unknown option ${arg}`);
|
|
@@ -2063,7 +2246,7 @@ export function run(
|
|
|
2063
2246
|
const dir = resolve(options.kit ?? env.DESIGN_KIT_DIR ?? defaultDir);
|
|
2064
2247
|
const kit = loadKit(dir);
|
|
2065
2248
|
if (command === "info") {
|
|
2066
|
-
io.out(infoText(kit));
|
|
2249
|
+
io.out(infoText(kit, options.json));
|
|
2067
2250
|
} else if (command === "find") {
|
|
2068
2251
|
if (!rest.length) throw new KitError("find needs a query");
|
|
2069
2252
|
io.out(findLines(kit, rest.join(" ")).join("\n"));
|
|
@@ -71,7 +71,7 @@ is missing.
|
|
|
71
71
|
## 3. Find what is real
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
|
-
node "$KIT/kit.mjs" find button # ≤ 30 lines: id
|
|
74
|
+
node "$KIT/kit.mjs" find button # ≤ 30 lines: id (widths grouped), size, title
|
|
75
75
|
node "$KIT/kit.mjs" show route:home@1280 # path, size and an outline with slots
|
|
76
76
|
```
|
|
77
77
|
|