@intentic/extension-ui 1.345.0 → 1.346.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 +20 -2
- package/check/cli.mjs +61 -0
- package/check/extension.d.mts +18 -0
- package/check/extension.mjs +166 -0
- package/check/kit-decisions.mjs +278 -0
- package/check/scan.mjs +42 -0
- package/dist/index.js +5 -0
- package/dist/types/extension-ui/src/index.d.ts +1 -1
- package/dist/types/ui/src/components/feedback/Await.vue.d.ts +36 -0
- package/dist/types/ui/src/components/feedback/InfoHint.vue.d.ts +1 -0
- package/dist/types/ui/src/components/forms/InlineRename.vue.d.ts +15 -0
- package/dist/types/ui/src/components/forms/SearchBar.vue.d.ts +1 -1
- package/dist/types/ui/src/components/layout/NavRail.vue.d.ts +0 -1
- package/dist/types/ui/src/components/rows/RowGroup.vue.d.ts +8 -8
- package/dist/types/ui/src/components/rows/RowNote.vue.d.ts +35 -0
- package/dist/types/ui/src/components/rows/Section.vue.d.ts +27 -0
- package/dist/types/ui/src/components/rows/SectionHeader.vue.d.ts +27 -0
- package/dist/types/ui/src/components/rows/row.d.ts +18 -0
- package/dist/types/ui/src/index.d.ts +5 -0
- package/dist/types/ui/src/lib/ui.d.ts +3 -0
- package/names.d.mts +22 -0
- package/names.mjs +73 -3
- package/package.json +9 -3
- package/src/index.ts +16 -5
package/README.md
CHANGED
|
@@ -27,7 +27,22 @@ flowchart LR
|
|
|
27
27
|
`engines.intentic` range the host satisfies then finds every name it was built with. One that asks for a name anyway
|
|
28
28
|
is stopped before it links: the editor's loader reads the bundle's imports and says which name is missing and that
|
|
29
29
|
the extension needs updating, where the browser would only say the module "does not provide an export named …".
|
|
30
|
-
-
|
|
30
|
+
- What an extension may no longer take from the kit is recorded once, in `names.mjs`: `extensionUiDeprecated` for
|
|
31
|
+
names that still link (`RepoRail` → `RepoPicker`, `errorMessage` → `messageOr`, and from 2.33.0 `SkeletonSnapshot`,
|
|
32
|
+
`vSkeletonSource` and `useLoadingReveal` → `Await`, all removable in 3.0.0), and `extensionUiRetired` for props and
|
|
33
|
+
recipe members that no longer do anything a view should rely on (from 2.34.0 `RowGroup`'s `count`, `caption` and
|
|
34
|
+
`flat`, `NavRail`'s `stickyHeadings`, and `ui.sectionLabel` drawn by hand → `Section`/`SectionHeader`). Each entry
|
|
35
|
+
carries the extension API version it arrived in, and `surface.json` records the list per version (`uiKitDeprecated`).
|
|
36
|
+
- `check/` is the kit's own judgement of an extension, published with it. `intentic-kit-check` (check/cli.mjs) reads an
|
|
37
|
+
extension's `src/` and fails on a deprecated name imported, a retired prop or member used, the decided looks the
|
|
38
|
+
editor is held to (check/kit-decisions.mjs: no header decision at a call site, no header drawn by hand, no card in a
|
|
39
|
+
card, no unspaced sections, no row height, no icon nudged by hand), and an `engines.intentic` floor below the version
|
|
40
|
+
that brought a name the extension imports, read off the installed extension-api's `surface.json`. Every extension
|
|
41
|
+
runs it in its `typecheck`; the monorepo's `extension-kit` check runs the same code over `_extensions/*` and the
|
|
42
|
+
standalone checkouts beside this repository, and `row-tiers`/`mark-alignment` read the same rules. A finding that is
|
|
43
|
+
right to keep says so in place with `allow(kit-check): <reason>`. (2026-10-20: the extensions shipped from their own
|
|
44
|
+
repositories still wired their waits by hand and wrote captions beside headers after the kit had decided both,
|
|
45
|
+
because these rules reached only this repository's templates.)
|
|
31
46
|
(2026-10-15: `RepoRail` left the kit on 2026-10-12 with no version moving, and every installed extension still
|
|
32
47
|
importing it stopped loading. It is back unchanged, and nothing recorded the kit's names before this.)
|
|
33
48
|
- `./diff`, `./format`, `./i18n` and `./worker` skip the component barrel, for extension tests that run without a Vue
|
|
@@ -41,11 +56,14 @@ flowchart LR
|
|
|
41
56
|
|
|
42
57
|
- [src/index.ts](src/index.ts) — the kit: every name an extension may import, with usage notes.
|
|
43
58
|
- [scripts/build.mjs](scripts/build.mjs) — builds the published types and the host bridge from `_editor/ui`.
|
|
44
|
-
- [names.mjs](names.mjs) — the runtime export names the shims and the host check read, and the
|
|
59
|
+
- [names.mjs](names.mjs) — the runtime export names the shims and the host check read, the deprecated ones and the retired props.
|
|
60
|
+
- [check/extension.mjs](check/extension.mjs) — what an extension must change for the kit it builds against; [check/cli.mjs](check/cli.mjs) is `intentic-kit-check`.
|
|
45
61
|
- [../../_editor/web/src/extension-host/hostModules.ts](../../_editor/web/src/extension-host/hostModules.ts) — where the shell publishes its instances to extension bundles.
|
|
46
62
|
|
|
47
63
|
## Commands
|
|
48
64
|
|
|
49
65
|
```sh
|
|
50
66
|
pnpm --filter @intentic/extension-ui build
|
|
67
|
+
node _tools/checks/extension-kit.mjs # every workspace extension against the kit as it stands
|
|
68
|
+
npx intentic-kit-check # inside an extension's own repository
|
|
51
69
|
```
|
package/check/cli.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `intentic-kit-check [dir]`: what an extension must change for the kit and API it builds against (check/extension.mjs),
|
|
3
|
+
// run from the extension's own repository, where the intentic repository's checks never reach. Reads the extension's
|
|
4
|
+
// `src/` and `intentic-extension.json`, and the surface record of the @intentic/extension-api it has installed. Exits 1
|
|
5
|
+
// with one `path:line why` per finding, so an author's typecheck fails where the host would otherwise refuse or the
|
|
6
|
+
// look would drift. A finding that is right to keep says so in place: `allow(kit-check): <reason>`.
|
|
7
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
|
+
import { join, relative, resolve } from "node:path";
|
|
10
|
+
import { extensionFindings } from "./extension.mjs";
|
|
11
|
+
|
|
12
|
+
const root = resolve(process.argv[2] ?? `.`);
|
|
13
|
+
const SOURCE = /\.(?:vue|ts|mts|js|mjs)$/u;
|
|
14
|
+
const SKIPPED = /\.(?:test|spec)\.\w+$|\.d\.m?ts$/u;
|
|
15
|
+
|
|
16
|
+
const files = [];
|
|
17
|
+
const walk = (dir) => {
|
|
18
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
19
|
+
const full = join(dir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
if (entry.name !== `node_modules` && entry.name !== `dist` && !entry.name.startsWith(`.`)) {
|
|
22
|
+
walk(full);
|
|
23
|
+
}
|
|
24
|
+
} else if (SOURCE.test(entry.name) && !SKIPPED.test(entry.name)) {
|
|
25
|
+
files.push({ path: relative(root, full).replaceAll(`\\`, `/`), text: readFileSync(full, `utf8`) });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const manifestPath = join(root, `intentic-extension.json`);
|
|
31
|
+
if (!existsSync(manifestPath) || !existsSync(join(root, `src`))) {
|
|
32
|
+
console.error(`intentic-kit-check: ${root} is not an extension (no intentic-extension.json beside a src/)`);
|
|
33
|
+
process.exit(2);
|
|
34
|
+
}
|
|
35
|
+
walk(join(root, `src`));
|
|
36
|
+
|
|
37
|
+
// The API this extension builds against, as installed beside it: its record of every version's surface.
|
|
38
|
+
// Undefined only when no extension-api that exports the record is installed; anything else is a real failure.
|
|
39
|
+
const NOT_INSTALLED = new Set([`MODULE_NOT_FOUND`, `ERR_MODULE_NOT_FOUND`, `ERR_PACKAGE_PATH_NOT_EXPORTED`]);
|
|
40
|
+
const surfaceOf = () => {
|
|
41
|
+
let path;
|
|
42
|
+
try {
|
|
43
|
+
path = createRequire(join(root, `package.json`)).resolve(`@intentic/extension-api/surface.json`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (NOT_INSTALLED.has(error?.code)) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
return JSON.parse(readFileSync(path, `utf8`));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const findings = extensionFindings({ files, manifest: JSON.parse(readFileSync(manifestPath, `utf8`)), surface: surfaceOf() });
|
|
54
|
+
if (findings.length > 0) {
|
|
55
|
+
for (const { at, why } of findings) {
|
|
56
|
+
console.error(`${at} ${why}`);
|
|
57
|
+
}
|
|
58
|
+
console.error(`\nintentic-kit-check: ${findings.length} problem(s) in what this extension takes from the kit`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
console.log(`intentic-kit-check: ${files.length} source files use the kit as it stands, within what engines.intentic promises`);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface KitFinding {
|
|
2
|
+
/** `path:line` relative to the extension's root, or `intentic-extension.json`. */
|
|
3
|
+
readonly at: string;
|
|
4
|
+
readonly why: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** One version's record in extension-api's surface.json, the two grains a bundle links by name. */
|
|
8
|
+
export interface RecordedLinks {
|
|
9
|
+
readonly uiKitExports?: readonly string[];
|
|
10
|
+
readonly moduleExports?: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** What an extension's sources must change for the kit and API it builds against (check/extension.mjs). */
|
|
14
|
+
export declare const extensionFindings: (input: {
|
|
15
|
+
readonly files: readonly { readonly path: string; readonly text: string }[];
|
|
16
|
+
readonly manifest: { readonly engines?: { readonly intentic?: string } } | undefined;
|
|
17
|
+
readonly surface: Readonly<Record<string, RecordedLinks>> | undefined;
|
|
18
|
+
}) => KitFinding[];
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// WHAT AN EXTENSION MUST CHANGE FOR THE KIT IT BUILDS AGAINST, judged where the extension is built. The kit's decisions
|
|
2
|
+
// and deprecations were enforced only on the templates inside the intentic repository, so an extension shipped from its
|
|
3
|
+
// own repository kept a retired look (a skeleton wired by hand, a caption beside a header, an icon nudged by a pixel) or
|
|
4
|
+
// imported a name its `engines.intentic` floor did not promise, with nothing saying so until a host refused it. This
|
|
5
|
+
// reads an extension's sources against the same records the host is held to:
|
|
6
|
+
// - a deprecated kit name imported (names.mjs `extensionUiDeprecated`), with what replaced it;
|
|
7
|
+
// - a retired prop passed or recipe member called (names.mjs `extensionUiRetired`), with why;
|
|
8
|
+
// - the kit's decided looks (kit-decisions.mjs: a header decision at the call site, a header drawn by hand, a card in a
|
|
9
|
+
// card, sections nothing spaces, a row's own height, an icon nudged by hand);
|
|
10
|
+
// - a name imported from the kit or the API that the manifest's `engines.intentic` floor does not have, read off the
|
|
11
|
+
// API's surface record (surface.json), so a bundle never reaches a host whose link would fail.
|
|
12
|
+
// Pure: the caller hands in the files, the manifest and the surface record. check/cli.mjs is the command around it.
|
|
13
|
+
import { extensionUiDeprecated, extensionUiNames, extensionUiRetired } from "../names.mjs";
|
|
14
|
+
import { glyphFindings, sectionFindings } from "./kit-decisions.mjs";
|
|
15
|
+
import { blank } from "./scan.mjs";
|
|
16
|
+
|
|
17
|
+
const KIT = `@intentic/extension-ui`;
|
|
18
|
+
const API = `@intentic/extension-api`;
|
|
19
|
+
|
|
20
|
+
// A finding at a line excused by `allow(kit-check): <reason>` on that line or the one above it, in any comment form.
|
|
21
|
+
const ALLOW = /allow\(kit-check\):\s*\S/u;
|
|
22
|
+
const excused = (lines, line) => ALLOW.test(lines[line - 1] ?? ``) || ALLOW.test(lines[line - 2] ?? ``);
|
|
23
|
+
|
|
24
|
+
const lineOf = (text, index) => text.slice(0, index).split(`\n`).length;
|
|
25
|
+
|
|
26
|
+
// The value names one `import { … } from "<pkg>[/sub]"` brings in (type-only imports and members link nothing).
|
|
27
|
+
const IMPORT = /import\s+(type\s+)?\{([^}]*)\}\s*from\s*["'](@intentic\/extension-(?:ui|api))(?:\/[\w-]+)?["']/gu;
|
|
28
|
+
const importsOf = (text) =>
|
|
29
|
+
[...text.matchAll(IMPORT)].flatMap((match) =>
|
|
30
|
+
match[1] !== undefined
|
|
31
|
+
? []
|
|
32
|
+
: match[2]
|
|
33
|
+
.split(`,`)
|
|
34
|
+
.map((entry) => entry.trim())
|
|
35
|
+
.filter((entry) => entry !== `` && !entry.startsWith(`type `))
|
|
36
|
+
.map((entry) => ({ name: entry.split(/\s+as\s+/u)[0].trim(), from: match[3], index: match.index })),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// `^2.34.0` or `2.34.0`, the two forms the host's matcher (extension-api engines.ts) accepts; its floor, or undefined.
|
|
40
|
+
const floorOf = (range) => {
|
|
41
|
+
const match = /^\s*\^?\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?\s*$/u.exec(range ?? ``);
|
|
42
|
+
return match === null ? undefined : `${match[1]}.${match[2] ?? 0}.${match[3] ?? 0}`;
|
|
43
|
+
};
|
|
44
|
+
const parts = (version) => version.split(`.`).map(Number);
|
|
45
|
+
const compare = (a, b) => {
|
|
46
|
+
const [x, y] = [parts(a), parts(b)];
|
|
47
|
+
return x[0] - y[0] || x[1] - y[1] || x[2] - y[2];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// The first recorded version each name of one grain appears in, and the version the grain was first recorded at (a name
|
|
51
|
+
// already there may be older than the record, so it says nothing about a floor below it).
|
|
52
|
+
const firstSeen = (surface, grain) => {
|
|
53
|
+
const versions = Object.keys(surface)
|
|
54
|
+
.filter((version) => Array.isArray(surface[version]?.[grain]))
|
|
55
|
+
.toSorted(compare);
|
|
56
|
+
const seen = new Map();
|
|
57
|
+
for (const version of versions) {
|
|
58
|
+
for (const name of surface[version][grain]) {
|
|
59
|
+
if (!seen.has(name)) {
|
|
60
|
+
seen.set(name, version);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { seen, start: versions[0] };
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const DEPRECATED = new Map(extensionUiDeprecated.map((entry) => [entry.name, entry]));
|
|
68
|
+
const KIT_NAMES = new Set(extensionUiNames);
|
|
69
|
+
// A recipe member called by its name (`ui.sectionLabel(`), from a template binding or a script alike.
|
|
70
|
+
const MEMBERS = extensionUiRetired
|
|
71
|
+
.filter((entry) => entry.name.startsWith(`ui.`))
|
|
72
|
+
.map((entry) => ({ ...entry, pattern: new RegExp(`\\b${entry.name.replace(`.`, `\\.`)}(?:Sm)?\\b`, `gu`) }));
|
|
73
|
+
|
|
74
|
+
const MANIFEST = `intentic-extension.json`;
|
|
75
|
+
|
|
76
|
+
// One file's findings at a source offset, unless the line (or the one above it) excuses them.
|
|
77
|
+
const sourceFindings = ({ path, text }, found) => {
|
|
78
|
+
const lines = text.split(`\n`);
|
|
79
|
+
return found
|
|
80
|
+
.map(({ index, why }) => ({ line: lineOf(text, index), why }))
|
|
81
|
+
.filter(({ line }) => !excused(lines, line))
|
|
82
|
+
.map(({ line, why }) => ({ at: `${path}:${line}`, why }));
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// A deprecated kit name, or one the kit does not have, imported.
|
|
86
|
+
const importFindings = (file) =>
|
|
87
|
+
sourceFindings(
|
|
88
|
+
file,
|
|
89
|
+
importsOf(file.text)
|
|
90
|
+
.filter(({ from }) => from === KIT)
|
|
91
|
+
.flatMap(({ name, index }) => {
|
|
92
|
+
const deprecated = DEPRECATED.get(name);
|
|
93
|
+
return [
|
|
94
|
+
...(deprecated === undefined
|
|
95
|
+
? []
|
|
96
|
+
: [{ index, why: `\`${name}\` is deprecated since extension API ${deprecated.since}, removable in ${deprecated.removableIn}: use \`${deprecated.use}\`` }]),
|
|
97
|
+
...(KIT_NAMES.has(name) ? [] : [{ index, why: `\`${name}\` is not a name the kit exports, so the bundle fails to link: see names.mjs in ${KIT}` }]),
|
|
98
|
+
];
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// A retired recipe member called, in a template binding or a script.
|
|
103
|
+
const memberFindings = (file) =>
|
|
104
|
+
sourceFindings(
|
|
105
|
+
file,
|
|
106
|
+
MEMBERS.flatMap((member) =>
|
|
107
|
+
[...file.text.matchAll(member.pattern)].map((match) => ({ index: match.index, why: `\`${member.name}\` is retired since extension API ${member.since}: ${member.why}` })),
|
|
108
|
+
),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// The kit's decided looks, read off a template.
|
|
112
|
+
const templateFindings = ({ path, text }) => {
|
|
113
|
+
if (!path.endsWith(`.vue`)) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const lines = text.split(`\n`);
|
|
117
|
+
const scan = blank(text);
|
|
118
|
+
return [...sectionFindings(path, scan), ...glyphFindings(path, scan)].filter(
|
|
119
|
+
(finding) => !excused(lines, Number(finding.at.slice(finding.at.lastIndexOf(`:`) + 1))),
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Whether `engines.intentic` is a range the host reads, and whether its floor has every name the sources import.
|
|
124
|
+
const enginesFindings = (files, range, surface) => {
|
|
125
|
+
const floor = floorOf(range);
|
|
126
|
+
if (range === undefined) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
if (floor === undefined) {
|
|
130
|
+
return [{ at: MANIFEST, why: `engines.intentic "${range}" is neither an exact version nor a caret range, the two the host reads: it refuses the extension` }];
|
|
131
|
+
}
|
|
132
|
+
const imports = files.flatMap((file) => importsOf(file.text).map((entry) => ({ ...entry, file })));
|
|
133
|
+
if (surface === undefined) {
|
|
134
|
+
return imports.length === 0
|
|
135
|
+
? []
|
|
136
|
+
: [{ at: MANIFEST, why: `${API}'s surface record (surface.json) could not be read, so whether engines.intentic "${range}" promises every name imported is unjudged: install the extension's dependencies` }];
|
|
137
|
+
}
|
|
138
|
+
const newest = Object.keys(surface).toSorted(compare).at(-1);
|
|
139
|
+
if (surface[floor] === undefined && compare(floor, newest) > 0) {
|
|
140
|
+
return [{ at: MANIFEST, why: `engines.intentic "${range}" asks for an extension API newer than the ${API} installed here records: update ${API}` }];
|
|
141
|
+
}
|
|
142
|
+
const grains = { [KIT]: firstSeen(surface, `uiKitExports`), [API]: firstSeen(surface, `moduleExports`) };
|
|
143
|
+
// Names the floor's host lacks, each with where it is imported and the version that brought it. A name already there
|
|
144
|
+
// when its grain was first recorded may be older than the record, so it says nothing about a floor below it.
|
|
145
|
+
const needs = imports
|
|
146
|
+
.map(({ name, from, index, file }) => ({ name, at: `${file.path}:${lineOf(file.text, index)}`, since: grains[from].seen.get(name), start: grains[from].start }))
|
|
147
|
+
.filter(({ since, start }) => since !== undefined && since !== start && compare(since, floor) > 0);
|
|
148
|
+
const highest = needs.map(({ since }) => since).toSorted(compare).at(-1);
|
|
149
|
+
if (highest === undefined) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
const lacked = needs.map(({ name, at, since }) => `\`${name}\` (${at}, from ${since})`).join(`, `);
|
|
153
|
+
return [{ at: MANIFEST, why: `engines.intentic "${range}" admits hosts from ${floor}, which lack ${lacked}: such a host refuses the bundle at link, so raise it to "^${highest}"` }];
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Every place an extension's sources leave the kit's decisions or its engines floor, as `{ at: "path:line", why }`.
|
|
158
|
+
* `files` are `{ path, text }` (.vue and .ts/.mts/.js/.mjs; tests and declarations are the caller's to leave out),
|
|
159
|
+
* `manifest` the parsed intentic-extension.json, `surface` the parsed surface.json of the extension API it builds
|
|
160
|
+
* against, or undefined when that cannot be read (the engines rule then says so instead of judging).
|
|
161
|
+
*/
|
|
162
|
+
export const extensionFindings = ({ files, manifest, surface }) =>
|
|
163
|
+
[
|
|
164
|
+
...enginesFindings(files, manifest?.engines?.intentic, surface),
|
|
165
|
+
...files.flatMap((file) => [...importFindings(file), ...memberFindings(file), ...templateFindings(file)]),
|
|
166
|
+
].toSorted((a, b) => a.at.localeCompare(b.at, undefined, { numeric: true }));
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// THE KIT'S DECIDED LOOKS, read off one template: the rules row-tiers, mark-alignment, kit-substitutes and recipe-tiers
|
|
2
|
+
// judge every view by, in one place so their tests read the same functions the checks run. Each refuses a decision the
|
|
3
|
+
// owner made once, and had re-made view by view while a view could still state it: a header's count, caption, fill or
|
|
4
|
+
// spacing; a card laid in a card; rows of different heights; an icon nudged onto its line by a pixel that is right for
|
|
5
|
+
// one type size only; a section label, a tab strip, a search field or an open row drawn by hand; a chip's own box.
|
|
6
|
+
import { extensionUiRetired } from "../names.mjs";
|
|
7
|
+
import { at, classWordsOf, tags, VOID } from "./scan.mjs";
|
|
8
|
+
|
|
9
|
+
// ── Sections ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
// The look the header owns (`ui.sectionLabel`, drawn only by <SectionHeader>): an uppercase, tracked label, spelled out or
|
|
12
|
+
// called for by the recipe's own name.
|
|
13
|
+
const HEADER_LOOK = (words) =>
|
|
14
|
+
/\bui\.sectionLabel(?:Sm)?\b/u.test(words) ||
|
|
15
|
+
(/(?:^|\s)uppercase(?:\s|$)/u.test(words) && /(?:^|\s)tracking-(?:wide|wider|widest)(?:\s|$)/u.test(words));
|
|
16
|
+
const SECTIONS = new Set([`RowGroup`, `Section`]);
|
|
17
|
+
// A parent that spaces what it holds: a gap, a vertical rhythm, or hairlines between them.
|
|
18
|
+
const SPACED = /(?:^|\s)(?:[\w@-]+:)*(?:gap-(?:y-)?[\d.[]|space-y-|divide-y)/u;
|
|
19
|
+
// A height on a row, at any breakpoint.
|
|
20
|
+
const ROW_HEIGHT = /(?:^|\s)(?:[\w@-]+:)*(?:min-h|max-h|h)-(?!0(?:\s|$))[\w.[\]/-]+/u;
|
|
21
|
+
const ELSE = /(?:^|\s)v-else(?:-if)?(?:[\s=>]|$)/u;
|
|
22
|
+
|
|
23
|
+
// What each removed prop was for, said where a call site still passes it: `extensionUiRetired` in names.mjs, the one
|
|
24
|
+
// record of them, whose `Component.prop` entries are read here under both spellings a template may use.
|
|
25
|
+
const kebab = (name) => name.replace(/[A-Z]/gu, (letter) => `-${letter.toLowerCase()}`);
|
|
26
|
+
const RETIRED_PROPS = {};
|
|
27
|
+
for (const { name, why } of extensionUiRetired) {
|
|
28
|
+
const [component, prop] = name.split(`.`);
|
|
29
|
+
if (/^[A-Z]/u.test(component) && prop !== undefined) {
|
|
30
|
+
RETIRED_PROPS[component] ??= {};
|
|
31
|
+
RETIRED_PROPS[component][prop] = why;
|
|
32
|
+
RETIRED_PROPS[component][kebab(prop)] = why;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// One walk over a template's open tags, with the stack of elements each sits in: `open` judges a tag (and says whether to
|
|
37
|
+
// push it as a frame), `close` judges a frame as it closes, the SFC's own `<template>` included.
|
|
38
|
+
const walk = (scan, open, close = () => {}) => {
|
|
39
|
+
const stack = [];
|
|
40
|
+
for (const tag of tags(scan)) {
|
|
41
|
+
if (tag.closing !== ``) {
|
|
42
|
+
const top = stack.findLastIndex((frame) => frame.name === tag.name);
|
|
43
|
+
if (top !== -1) {
|
|
44
|
+
stack.splice(top).forEach(close);
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const frame = open(tag, stack);
|
|
49
|
+
if (frame !== undefined && tag.selfClosing === `` && !VOID.has(tag.name)) {
|
|
50
|
+
stack.push(frame);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
stack.splice(0).forEach(close);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// `<template v-if>`/`v-for` draw nothing, so what is inside is still its parent's child.
|
|
57
|
+
const parentOf = (stack) => stack.findLast((frame) => !frame.transparent);
|
|
58
|
+
|
|
59
|
+
// ── 4 · a header decision taken at the call site
|
|
60
|
+
const retiredProps = (name, attrs) => {
|
|
61
|
+
const retired = RETIRED_PROPS[name] ?? {};
|
|
62
|
+
return [...attrs.matchAll(/(?:^|\s)(?::|v-bind:)?([\w-]+)(?=[\s=>]|$)/gu)]
|
|
63
|
+
.filter((match) => retired[match[1]] !== undefined)
|
|
64
|
+
.map((match) => `<${name}> takes no \`${match[1]}\`: ${retired[match[1]]}`);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ── 8, 10 · a group among another group's rows, a row's own height (a header drawn by hand, once rule 7 here, is
|
|
68
|
+
// `substituteFindings`' first rule, on every element rather than only on `<h1>`–`<h6>`)
|
|
69
|
+
const redecidedLooks = (name, words, parent) => [
|
|
70
|
+
...(name === `RowGroup` && parent?.name === `RowGroup`
|
|
71
|
+
? [
|
|
72
|
+
`a <RowGroup> laid straight among another group's rows nests one card in another: put groups side by side in a stack, or the inner one in a <RowNote variant="block"> where it lies on the outer card`,
|
|
73
|
+
]
|
|
74
|
+
: []),
|
|
75
|
+
...((name === `Row` || name === `DisclosureRow`) && ROW_HEIGHT.test(words)
|
|
76
|
+
? [
|
|
77
|
+
`<${name}> is given a height of its own, so it stands taller or shorter than the rows beside it: a row is as tall as its tier, and a settings group whose rows share one height says so once with \`equal-rows\``,
|
|
78
|
+
]
|
|
79
|
+
: []),
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
// How many sections a tag adds to its parent's stack (rule 9): one drawn several times (`v-for`) counts twice; an `else`
|
|
83
|
+
// branch draws instead of its `if`; one that states its own distance from what precedes it (`mt-*`) is spaced already.
|
|
84
|
+
const sectionWeight = (name, attrs, words) => {
|
|
85
|
+
if (!SECTIONS.has(name) || ELSE.test(attrs) || /(?:^|\s)m[ty]-(?!0(?:\s|$))/u.test(words)) {
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
return /(?:^|\s)v-for=/u.test(attrs) ? 2 : 1;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// ── 9 · sections side by side, judged as their parent closes: it states their gap, or it is the template's root
|
|
92
|
+
const unspaced = (frame) => {
|
|
93
|
+
if (frame.sections < 2 || (frame.name !== `#root` && SPACED.test(frame.words))) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
return frame.name === `#root`
|
|
97
|
+
? `this template is several <RowGroup>/<Section>s side by side at its root, so whatever mounts it decides whether anything spaces them: wrap them in one box that states the gap (\`flex flex-col gap-6\`)`
|
|
98
|
+
: `<${frame.name}> holds several <RowGroup>/<Section>s and states no gap between them, so their headers touch the section above: give it the stack's gap (\`flex flex-col gap-6\`)`;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A blanked template's section findings: a header decision taken at the call site (4), a group among another group's
|
|
103
|
+
* rows (8), sections side by side with nothing spacing them (9), a row given a height (10).
|
|
104
|
+
*/
|
|
105
|
+
export const sectionFindings = (path, scan) => {
|
|
106
|
+
const findings = [];
|
|
107
|
+
walk(
|
|
108
|
+
scan,
|
|
109
|
+
({ name, attrs, index }, stack) => {
|
|
110
|
+
const where = at(path, scan, index);
|
|
111
|
+
// The SFC's own `<template>` is the root every top-level element is a child of.
|
|
112
|
+
if (stack.length === 0 && name === `template`) {
|
|
113
|
+
return { name: `#root`, transparent: false, words: ``, sections: 0, at: where };
|
|
114
|
+
}
|
|
115
|
+
const words = classWordsOf(attrs);
|
|
116
|
+
const parent = parentOf(stack);
|
|
117
|
+
for (const why of [...retiredProps(name, attrs), ...redecidedLooks(name, words, parent)]) {
|
|
118
|
+
findings.push({ at: where, why });
|
|
119
|
+
}
|
|
120
|
+
if (parent !== undefined) {
|
|
121
|
+
parent.sections += sectionWeight(name, attrs, words);
|
|
122
|
+
}
|
|
123
|
+
return { name, transparent: name === `template` && !/(?:^|\s)(?:#|v-slot)/u.test(attrs), words, sections: 0, at: where };
|
|
124
|
+
},
|
|
125
|
+
(frame) => {
|
|
126
|
+
const why = unspaced(frame);
|
|
127
|
+
if (why !== undefined) {
|
|
128
|
+
findings.push({ at: frame.at, why });
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
return findings;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// ── Glyphs ───────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
// A utility that moves a box vertically, at any breakpoint.
|
|
138
|
+
const ICON_OFFSET =
|
|
139
|
+
/(?:^|\s)(?:[\w@-]+:)*-?(?:mt|mb|my|translate-y|top)-[\w./[\]-]+|(?:^|\s)(?:[\w@-]+:)*align-(?:top|middle|bottom|text-top|text-bottom|sub|super|baseline|\[[^\]]*\])/u;
|
|
140
|
+
const POSITIONED = /(?:^|\s)(?:absolute|fixed)(?:\s|$)/u;
|
|
141
|
+
const SIZE = /(?:^|\s)((?:[\w@-]+:)*text-(?:4xs|3xs|2xs|xs|sm|base|lg|xl|2xl|\[[\d.]+rem\]))(?=\s|$)/gu;
|
|
142
|
+
const sizesOf = (words) =>
|
|
143
|
+
[...words.matchAll(SIZE)]
|
|
144
|
+
.map((match) => match[1])
|
|
145
|
+
.toSorted()
|
|
146
|
+
.join(` `);
|
|
147
|
+
|
|
148
|
+
// An `<Icon>` nudged by hand: a vertical utility on it that is not the placement of a positioned box.
|
|
149
|
+
const nudgeOf = (words) => (POSITIONED.test(words) ? undefined : ICON_OFFSET.exec(words)?.[0].trim());
|
|
150
|
+
|
|
151
|
+
// The first words after a glyph leading an `items-start` line, when they state a type size the line does not.
|
|
152
|
+
const misreadLine = (line, words) => {
|
|
153
|
+
const stated = sizesOf(words);
|
|
154
|
+
const own = sizesOf(line.words);
|
|
155
|
+
return stated === `` || stated === own
|
|
156
|
+
? undefined
|
|
157
|
+
: `the words this <Icon> leads state \`${stated}\` and their \`items-start\` line ${own === `` ? `states none` : `states \`${own}\``}: \`.ui-icon\` reads the line's height where its type is stated, so state the size on the line (the words inherit it)`;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* A blanked template's glyph findings: an `<Icon>` nudged by hand, and a glyph-led `items-start` line whose first words
|
|
162
|
+
* state a type size the line does not (`.ui-icon` reads the line's height where its type is stated).
|
|
163
|
+
*/
|
|
164
|
+
export const glyphFindings = (path, scan) => {
|
|
165
|
+
const findings = [];
|
|
166
|
+
walk(scan, ({ name, attrs, index }, stack) => {
|
|
167
|
+
const words = classWordsOf(attrs);
|
|
168
|
+
const holder = stack.findLast((frame) => frame.name !== `template`);
|
|
169
|
+
const nudge = name === `Icon` ? nudgeOf(words) : undefined;
|
|
170
|
+
const why =
|
|
171
|
+
nudge === undefined
|
|
172
|
+
? undefined
|
|
173
|
+
: `\`${nudge}\` nudges an <Icon> by hand: \`.ui-icon\` sits on the text it labels, and leading an \`items-start\` line it centres on the first row itself, at every size`;
|
|
174
|
+
// A line is read once, at the first element after its glyph: the words whose line the glyph centres on.
|
|
175
|
+
const led = holder?.glyphLed === true && holder.read !== true && name !== `Icon` && name !== `template`;
|
|
176
|
+
for (const said of [why, led ? misreadLine(holder, words) : undefined]) {
|
|
177
|
+
if (said !== undefined) {
|
|
178
|
+
findings.push({ at: at(path, scan, index), why: said });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (holder !== undefined && holder.line) {
|
|
182
|
+
holder.glyphLed ||= name === `Icon` && holder.read !== true;
|
|
183
|
+
holder.read ||= led;
|
|
184
|
+
}
|
|
185
|
+
return { name, words, line: /(?:^|\s)items-start(?:\s|$)/u.test(words) };
|
|
186
|
+
});
|
|
187
|
+
return findings;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// ── Substitutes ──────────────────────────────────────────────────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
// Each kit primitive below was drawn by hand in a view before it existed, and was drawn by hand again after, by a view
|
|
193
|
+
// that did not know it did. Where a rule's primitive is defined, the rule does not apply: it is that primitive's own
|
|
194
|
+
// markup. `<th>`/`<tr>` are a table's column heads, which InfoTable draws in the label's type on purpose.
|
|
195
|
+
const OWNERS = {
|
|
196
|
+
label: /(?:^|\/)_editor\/ui\/src\/components\/rows\/SectionHeader\.vue$/u,
|
|
197
|
+
tabs: /(?:^|\/)_editor\/ui\/src\/components\/forms\/SegmentedControl\.vue$/u,
|
|
198
|
+
search: /(?:^|\/)_editor\/ui\/src\/components\/forms\/SearchBar\.vue$/u,
|
|
199
|
+
open: /(?:^|\/)_editor\/ui\/src\/components\/rows\/DisclosureRow\.vue$/u,
|
|
200
|
+
};
|
|
201
|
+
const TABLE_HEAD = new Set([`th`, `tr`, `thead`]);
|
|
202
|
+
const word = (name) => new RegExp(`(?:^|\\s)${name}(?:\\s|$)`, `u`);
|
|
203
|
+
const SEARCH_DRESS = /(?:^|\s)ui-search(?:-band|-icon)?(?:\s|$)/u;
|
|
204
|
+
const FIELD = new Set([`input`, `InputText`]);
|
|
205
|
+
|
|
206
|
+
const SAYS = {
|
|
207
|
+
label: (name) =>
|
|
208
|
+
`<${name}> wears the section label's look (uppercase, tracked) by hand, so its size, spacing and what sits beside it are this view's to drift: a label over a part is <SectionHeader> (\`density="dense"\` in a menu, a drawer or a card), or <Section :label> around what it heads; words inside a row are not a heading and take no uppercase`,
|
|
209
|
+
tabs: `a tab strip drawn by hand: <SegmentedControl> owns the roving tab stop, the arrow keys and the selected mark (\`variant="underline"\` for tabs that switch the parts of a page)`,
|
|
210
|
+
search: `a search field drawn by hand: <SearchBar> (\`variant="panel"\` as a popover's first band, \`field\` above a list) owns the magnifier, the clear, Escape and the busy spin`,
|
|
211
|
+
open: `the open row's wash put on by hand: an expanding row is <DisclosureRow>, which owns \`ui-row-open\` behind its header and drawer as one surface`,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A blanked template's hand-drawn substitutes for a kit primitive, each as `{ index, rule, why }`: the section label's
|
|
216
|
+
* look on any element (`label`), a tab strip (`tabs`), a search field (`search`), and the open row's wash (`open`).
|
|
217
|
+
* The caller turns `index` into a line and reads `allow(substitutes)` there.
|
|
218
|
+
*/
|
|
219
|
+
export const substituteFindings = (path, scan) => {
|
|
220
|
+
const findings = [];
|
|
221
|
+
const owned = (rule) => OWNERS[rule].test(path);
|
|
222
|
+
const add = (index, rule, why) => {
|
|
223
|
+
if (!owned(rule)) {
|
|
224
|
+
findings.push({ index, rule, why });
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
// The open tag before this one, for a field that a magnifier glyph leads.
|
|
228
|
+
let previous;
|
|
229
|
+
for (const { closing, name, attrs, index } of tags(scan)) {
|
|
230
|
+
if (closing !== ``) {
|
|
231
|
+
previous = undefined;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const words = classWordsOf(attrs);
|
|
235
|
+
if (HEADER_LOOK(words) && !TABLE_HEAD.has(name)) {
|
|
236
|
+
add(index, `label`, SAYS.label(name));
|
|
237
|
+
}
|
|
238
|
+
if (/(?:^|\s)role="tablist"/u.test(attrs) || /\bui\.tab\(/u.test(attrs)) {
|
|
239
|
+
add(index, `tabs`, SAYS.tabs);
|
|
240
|
+
}
|
|
241
|
+
const ledByMagnifier = previous?.name === `Icon` && /(?:^|\s)name="search"/u.test(previous.attrs);
|
|
242
|
+
if (SEARCH_DRESS.test(words) || (FIELD.has(name) && (/(?:^|\s)type="search"/u.test(attrs) || ledByMagnifier))) {
|
|
243
|
+
add(index, `search`, SAYS.search);
|
|
244
|
+
}
|
|
245
|
+
if (word(`ui-row-open`).test(words)) {
|
|
246
|
+
add(index, `open`, SAYS.open);
|
|
247
|
+
}
|
|
248
|
+
previous = { name, attrs };
|
|
249
|
+
}
|
|
250
|
+
return findings;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// ── Chips and pills ──────────────────────────────────────────────────────────────────────────────────────────────
|
|
254
|
+
|
|
255
|
+
// What a chip's or a pill's size owns: a padding, a height, a gap, a type size, a line height, a radius, at any variant.
|
|
256
|
+
const BOX_WORD =
|
|
257
|
+
/^(?:p[xytblrse]?-|(?:min-|max-)?h-|size-|gap(?:-[xy])?-|leading-|rounded(?:-|$)|text-(?:4xs|3xs|2xs|xs|sm|base|lg|xl|2xl|\[[\d.]+r?em\]))/u;
|
|
258
|
+
const bareWord = (token) => token.replace(/^!/u, ``).replace(/^(?:[\w@[\]&>*/-]+:)+/u, ``).replace(/^!/u, ``);
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* The words of a class list that restate a chip's or a status pill's box, which utilities.css decides once per size
|
|
262
|
+
* (`ui.chip({ size, lead, trail })`, `ui-status-pill[-md|-inline]`): `kind` is which of the two the list draws, if
|
|
263
|
+
* either. A `ui.chip(…)` call's own arguments are left out, since the recipe check reads those on their own.
|
|
264
|
+
*/
|
|
265
|
+
export const boxOverrides = (classes) => {
|
|
266
|
+
const words = classes.replace(/ui\.chip\((?:[^()]|\([^()]*\))*\)/gu, ` ui-chip `);
|
|
267
|
+
const kind = /(?:^|\s)ui-chip(?=\s|$)/u.test(words) ? `chip` : /(?:^|\s)ui-status-pill(?=\s|$)/u.test(words) ? `pill` : undefined;
|
|
268
|
+
if (kind === undefined) {
|
|
269
|
+
return { kind, words: [] };
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
kind,
|
|
273
|
+
words: words
|
|
274
|
+
.split(/\s+/u)
|
|
275
|
+
.map((token) => token.replace(/^[`'"([{]+|[`'")\]},]+$/gu, ``))
|
|
276
|
+
.filter((token) => BOX_WORD.test(bareWord(token))),
|
|
277
|
+
};
|
|
278
|
+
};
|
package/check/scan.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Markup reading without a parser, shared by the kit's own checks (check/kit-decisions.mjs, check/extension.mjs) and the
|
|
2
|
+
// intentic repository's template gates (_tools/checks/lib/templates.mjs re-exports it), so an extension built outside the
|
|
3
|
+
// repository is judged by the same walk as the editor. Needs nothing installed: checks run before `pnpm install`.
|
|
4
|
+
|
|
5
|
+
// Blanks, not strips, `<script>`, `<style>` and `<!-- -->` regions so line numbers stay valid; each one's example
|
|
6
|
+
// markup would otherwise misreport as real usage.
|
|
7
|
+
const blanked = (m) => m.replace(/[^\n]/gu, ` `);
|
|
8
|
+
export const blank = (source) =>
|
|
9
|
+
source
|
|
10
|
+
.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gu, blanked)
|
|
11
|
+
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gu, blanked)
|
|
12
|
+
.replace(/<!--[\s\S]*?-->/gu, blanked);
|
|
13
|
+
|
|
14
|
+
// A tag walk, not a parser: attribute values are consumed as units so a `>` inside `:class="{…}"` can't end a tag
|
|
15
|
+
// early. A generator, not a shared regex, since `/g` carries `lastIndex` and callers would fight over one cursor.
|
|
16
|
+
const TAG = /<(\/?)([A-Za-z][\w.-]*)((?:"[^"]*"|'[^']*'|`[^`]*`|[^>"'`])*?)(\/?)>/gu;
|
|
17
|
+
export function* tags(scan, from = 0) {
|
|
18
|
+
const walk = new RegExp(TAG.source, TAG.flags);
|
|
19
|
+
walk.lastIndex = from;
|
|
20
|
+
let match;
|
|
21
|
+
while ((match = walk.exec(scan)) !== null) {
|
|
22
|
+
const [, closing, name, attrs, selfClosing] = match;
|
|
23
|
+
yield { closing, name, attrs, selfClosing, index: match.index, end: walk.lastIndex };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Elements with no closing tag, so a tag walk must not push them onto its stack. */
|
|
28
|
+
export const VOID = new Set([`br`, `hr`, `img`, `input`, `source`]);
|
|
29
|
+
|
|
30
|
+
/** `path:line` for an offset into the blanked scan, which shares the source's newlines. */
|
|
31
|
+
export const at = (path, scan, index) => `${path}:${scan.slice(0, index).split(`\n`).length}`;
|
|
32
|
+
|
|
33
|
+
/** Both `class="…"` and `:class="…"`, joined: geometry hidden in a bound class is the same geometry. */
|
|
34
|
+
export const classesOf = (attrs) => [...attrs.matchAll(/:?class="([^"]*)"/gu)].map((m) => m[1]).join(` `);
|
|
35
|
+
|
|
36
|
+
// Turns punctuation into whitespace: a bound class is often an array of template literals, so its first class is
|
|
37
|
+
// preceded by a backtick, not a space, which a `(?:^|\s)`-anchored rule would otherwise miss.
|
|
38
|
+
export const classWordsOf = (attrs) =>
|
|
39
|
+
classesOf(attrs)
|
|
40
|
+
.replace(/[`',]/gu, ` `)
|
|
41
|
+
.replace(/\s+/gu, ` `)
|
|
42
|
+
.trim();
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export const AddressField = m["AddressField"];
|
|
|
14
14
|
export const AgentRunButton = m["AgentRunButton"];
|
|
15
15
|
export const AnchoredOverlay = m["AnchoredOverlay"];
|
|
16
16
|
export const Avatar = m["Avatar"];
|
|
17
|
+
export const Await = m["Await"];
|
|
17
18
|
export const BarChart = m["BarChart"];
|
|
18
19
|
export const BottomSheet = m["BottomSheet"];
|
|
19
20
|
export const BrandMark = m["BrandMark"];
|
|
@@ -36,6 +37,7 @@ export const ImageView = m["ImageView"];
|
|
|
36
37
|
export const InfoDialog = m["InfoDialog"];
|
|
37
38
|
export const InfoHint = m["InfoHint"];
|
|
38
39
|
export const InfoTable = m["InfoTable"];
|
|
40
|
+
export const InlineRename = m["InlineRename"];
|
|
39
41
|
export const Markdown = m["Markdown"];
|
|
40
42
|
export const MarkdownDocument = m["MarkdownDocument"];
|
|
41
43
|
export const MarkdownFigure = m["MarkdownFigure"];
|
|
@@ -60,8 +62,11 @@ export const ResizeSeam = m["ResizeSeam"];
|
|
|
60
62
|
export const ResponsiveOverlay = m["ResponsiveOverlay"];
|
|
61
63
|
export const Row = m["Row"];
|
|
62
64
|
export const RowGroup = m["RowGroup"];
|
|
65
|
+
export const RowNote = m["RowNote"];
|
|
63
66
|
export const ScrollFrame = m["ScrollFrame"];
|
|
64
67
|
export const SearchBar = m["SearchBar"];
|
|
68
|
+
export const Section = m["Section"];
|
|
69
|
+
export const SectionHeader = m["SectionHeader"];
|
|
65
70
|
export const SegmentedControl = m["SegmentedControl"];
|
|
66
71
|
export const SkeletonRows = m["SkeletonRows"];
|
|
67
72
|
export const SkeletonSnapshot = m["SkeletonSnapshot"];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AgentRunButton, type AgentRunAttempt, type AgentRunChoice, type AgentRunPicker, type ModelPicking, useAgentRunPick, type FixStanceLook, fixStanceLook, AnchoredOverlay, Avatar, Button, vAction, BarChart, BottomSheet, BrandMark, Card, clipboardOf, browserOwnsClick, appLink, openForwardedPort, ChangeStatusMark, type ChangeStatus, ui, type IconButtonVariants, type TextButtonVariants, type InputVariants, type ChipVariants, type SectionLabelVariants, type ButtonTier, type ButtonTone, Code, CodeField, ConfirmDialog, ContextMenu, OverflowActions, ActionSheet, type ActionItem, CopyButton, AddressField, StatusTally, type TallyItem, DagEditor, DagGraph, type DagEdge, type DagNode, DiffStat, DisclosureRow, FilterBar, Icon, type IconName, ImageView, InfoDialog, InfoHint, InfoTable, type FileFormat, formatOf, Markdown, MarkdownDocument, MarkdownFigure, Modal, type NavGroup, NavRail, NoteEditor, type NoteDraft, type NoteDraftOptions, useNoteDraft, useKeyedDraft, type NoticeModel, type NoticeTone, type NoticeAction, Notice, NoticeStack, Page, PageAction, PageHeader, ScrollFrame, PersonaFace, type PersonaLike, Picker, ProjectChip, type PickerGroup, type PickerOption, type PickerOptions, ProgressRing, RepoPicker, type RepoPickerAll, RepoRail, type RepoRailAll, type RepoRailGroup, type RepoRailRow, ProseField, ResizeSeam, ResponsiveOverlay, Row, RowGroup, SearchBar, SegmentedControl, seriesColor, sinceOf, useLoadingReveal, SkeletonRows, SkeletonSnapshot, vSkeletonSource, SplitView, StatStrip, StatusBadge, type StatusVariant, StepSection, timeWindows, type TimeWindow, timeWindowWords, useCompact, useDevice, useListNavigation, useNow, useLatest, messageOr, messageOr as errorMessage, noticeFrom, noticeOf, useAsyncAction, useNarrow, useRailMemory, useScrollReset, type StickyTop, useStickyTop, useTheme, } from "../../ui/src/index.js";
|
|
1
|
+
export { AgentRunButton, type AgentRunAttempt, type AgentRunChoice, type AgentRunPicker, type ModelPicking, useAgentRunPick, type FixStanceLook, fixStanceLook, AnchoredOverlay, Avatar, Button, vAction, BarChart, BottomSheet, BrandMark, Card, clipboardOf, browserOwnsClick, appLink, openForwardedPort, ChangeStatusMark, type ChangeStatus, ui, type IconButtonVariants, type TextButtonVariants, type InputVariants, type ChipVariants, type SectionLabelVariants, type ButtonTier, type ButtonTone, Code, CodeField, ConfirmDialog, ContextMenu, OverflowActions, ActionSheet, type ActionItem, CopyButton, AddressField, StatusTally, type TallyItem, DagEditor, DagGraph, type DagEdge, type DagNode, DiffStat, DisclosureRow, FilterBar, Icon, type IconName, ImageView, InfoDialog, InfoHint, InfoTable, InlineRename, type FileFormat, formatOf, Markdown, MarkdownDocument, MarkdownFigure, Modal, type NavGroup, NavRail, NoteEditor, type NoteDraft, type NoteDraftOptions, useNoteDraft, useKeyedDraft, type NoticeModel, type NoticeTone, type NoticeAction, Notice, NoticeStack, Page, PageAction, PageHeader, ScrollFrame, PersonaFace, type PersonaLike, Picker, ProjectChip, type PickerGroup, type PickerOption, type PickerOptions, ProgressRing, RepoPicker, type RepoPickerAll, RepoRail, type RepoRailAll, type RepoRailGroup, type RepoRailRow, ProseField, ResizeSeam, ResponsiveOverlay, Row, RowGroup, RowNote, Section, SectionHeader, SearchBar, SegmentedControl, seriesColor, sinceOf, useLoadingReveal, SkeletonRows, Await, SkeletonSnapshot, vSkeletonSource, SplitView, StatStrip, StatusBadge, type StatusVariant, StepSection, timeWindows, type TimeWindow, timeWindowWords, useCompact, useDevice, useListNavigation, useNow, useLatest, messageOr, messageOr as errorMessage, noticeFrom, noticeOf, useAsyncAction, useNarrow, useRailMemory, useScrollReset, type StickyTop, useStickyTop, useTheme, } from "../../ui/src/index.js";
|
|
2
2
|
export { formatBytes, formatClock, formatCompact, formatCount, formatDate, formatDateLong, formatDateTime, formatDayMonth, formatDayMonthTime, formatDuration, formatElapsed, formatMoney, formatPercent, formatTime, formatTimestamp, formatTokens, formatWeekdayTime, formatWhen, freshness, timeAgo, } from "./format.js";
|
|
3
3
|
export { type DiffMark, diffMark, type Signal, type TintWeight, type Tone, toneDot, toneFill, toneHover, toneInk, tonePlate, toneRim, toneTint, toneWash, } from "./tone.js";
|
|
4
4
|
export { diffSequence, pairEdits, similarity, wordDiff } from "./diff.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Loaded } from "@intentic/extension-api";
|
|
2
|
+
import { type VNode } from "vue";
|
|
3
|
+
declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
4
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<{
|
|
5
|
+
of: Loaded<T>;
|
|
6
|
+
imprint?: string | undefined;
|
|
7
|
+
label?: string | undefined;
|
|
8
|
+
}> & (typeof globalThis extends {
|
|
9
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
10
|
+
} ? P : {});
|
|
11
|
+
expose: (exposed: {}) => void;
|
|
12
|
+
attrs: any;
|
|
13
|
+
slots: {
|
|
14
|
+
default?(props: {
|
|
15
|
+
readonly data: T;
|
|
16
|
+
readonly refreshing: boolean;
|
|
17
|
+
readonly refreshError: string | undefined;
|
|
18
|
+
}): VNode[];
|
|
19
|
+
fallback?(): VNode[];
|
|
20
|
+
failed?(props: {
|
|
21
|
+
readonly error: string;
|
|
22
|
+
readonly retry: (() => void) | undefined;
|
|
23
|
+
}): VNode[];
|
|
24
|
+
idle?(): VNode[];
|
|
25
|
+
};
|
|
26
|
+
emit: {};
|
|
27
|
+
}>) => import("vue").VNode & {
|
|
28
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
29
|
+
};
|
|
30
|
+
declare const _default: typeof __VLS_export;
|
|
31
|
+
export default _default;
|
|
32
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
33
|
+
[K in keyof T]: T[K];
|
|
34
|
+
} : {
|
|
35
|
+
[K in keyof T as K]: T[K];
|
|
36
|
+
}) & {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
value: string | undefined;
|
|
3
|
+
write: (name: string) => Promise<void>;
|
|
4
|
+
label: string;
|
|
5
|
+
action?: string;
|
|
6
|
+
fallback?: string;
|
|
7
|
+
editable?: boolean;
|
|
8
|
+
maxlength?: number;
|
|
9
|
+
failure?: string;
|
|
10
|
+
};
|
|
11
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
12
|
+
begin: () => void;
|
|
13
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
@@ -20,7 +20,7 @@ type __VLS_Slots = {} & {
|
|
|
20
20
|
controls?: (props: typeof __VLS_6) => any;
|
|
21
21
|
};
|
|
22
22
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
23
|
-
focus: (select?: boolean) => void;
|
|
23
|
+
focus: (select?: boolean | `end`) => void;
|
|
24
24
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
25
25
|
clear: () => any;
|
|
26
26
|
"update:modelValue": (value: string) => any;
|
|
@@ -3,7 +3,6 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
|
|
|
3
3
|
props: import("vue").PublicProps & __VLS_PrettifyLocal<({
|
|
4
4
|
groups: readonly NavGroup<T>[];
|
|
5
5
|
filterable?: boolean;
|
|
6
|
-
stickyHeadings?: boolean;
|
|
7
6
|
placeholder?: string;
|
|
8
7
|
} & {
|
|
9
8
|
modelValue?: string;
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
+
import type { SectionLabelVariants } from "../../lib/ui.js";
|
|
1
2
|
import { type RowDensity } from "./row.js";
|
|
2
3
|
type __VLS_Props = {
|
|
3
4
|
label?: string;
|
|
4
|
-
|
|
5
|
-
flat?: boolean;
|
|
5
|
+
tone?: NonNullable<SectionLabelVariants[`tone`]>;
|
|
6
6
|
undivided?: boolean;
|
|
7
7
|
equalRows?: boolean;
|
|
8
8
|
density?: RowDensity;
|
|
9
9
|
};
|
|
10
|
-
declare var
|
|
10
|
+
declare var __VLS_8: {}, __VLS_11: {}, __VLS_14: {}, __VLS_16: {}, __VLS_24: {};
|
|
11
11
|
type __VLS_Slots = {} & {
|
|
12
|
-
label?: (props: typeof
|
|
12
|
+
label?: (props: typeof __VLS_8) => any;
|
|
13
13
|
} & {
|
|
14
|
-
info?: (props: typeof
|
|
14
|
+
info?: (props: typeof __VLS_11) => any;
|
|
15
15
|
} & {
|
|
16
|
-
actions?: (props: typeof
|
|
16
|
+
actions?: (props: typeof __VLS_14) => any;
|
|
17
17
|
} & {
|
|
18
|
-
lead?: (props: typeof
|
|
18
|
+
lead?: (props: typeof __VLS_16) => any;
|
|
19
19
|
} & {
|
|
20
|
-
default?: (props: typeof
|
|
20
|
+
default?: (props: typeof __VLS_24) => any;
|
|
21
21
|
};
|
|
22
22
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
23
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { IconName } from "../../icons/iconSets.js";
|
|
2
|
+
import { type RowTone } from "./row.js";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
variant?: `note` | `empty` | `action` | `block`;
|
|
5
|
+
lead?: `icon` | `face`;
|
|
6
|
+
icon?: IconName;
|
|
7
|
+
tone?: RowTone;
|
|
8
|
+
label?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
};
|
|
11
|
+
declare var __VLS_1: {}, __VLS_3: {
|
|
12
|
+
mark: 22 | 18 | 28 | 36;
|
|
13
|
+
}, __VLS_10: {}, __VLS_17: {};
|
|
14
|
+
type __VLS_Slots = {} & {
|
|
15
|
+
default?: (props: typeof __VLS_1) => any;
|
|
16
|
+
} & {
|
|
17
|
+
default?: (props: typeof __VLS_3) => any;
|
|
18
|
+
} & {
|
|
19
|
+
default?: (props: typeof __VLS_10) => any;
|
|
20
|
+
} & {
|
|
21
|
+
default?: (props: typeof __VLS_17) => any;
|
|
22
|
+
};
|
|
23
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
24
|
+
click: (event: MouseEvent) => any;
|
|
25
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
26
|
+
onClick?: ((event: MouseEvent) => any) | undefined;
|
|
27
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
28
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
29
|
+
declare const _default: typeof __VLS_export;
|
|
30
|
+
export default _default;
|
|
31
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
32
|
+
new (): {
|
|
33
|
+
$slots: S;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SectionLabelVariants } from "../../lib/ui.js";
|
|
2
|
+
import { type RowDensity } from "./row.js";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
label?: string;
|
|
5
|
+
tone?: NonNullable<SectionLabelVariants[`tone`]>;
|
|
6
|
+
heading?: 2 | 3 | 4;
|
|
7
|
+
density?: RowDensity;
|
|
8
|
+
};
|
|
9
|
+
declare var __VLS_8: {}, __VLS_11: {}, __VLS_14: {}, __VLS_16: {};
|
|
10
|
+
type __VLS_Slots = {} & {
|
|
11
|
+
label?: (props: typeof __VLS_8) => any;
|
|
12
|
+
} & {
|
|
13
|
+
info?: (props: typeof __VLS_11) => any;
|
|
14
|
+
} & {
|
|
15
|
+
actions?: (props: typeof __VLS_14) => any;
|
|
16
|
+
} & {
|
|
17
|
+
default?: (props: typeof __VLS_16) => any;
|
|
18
|
+
};
|
|
19
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
21
|
+
declare const _default: typeof __VLS_export;
|
|
22
|
+
export default _default;
|
|
23
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
24
|
+
new (): {
|
|
25
|
+
$slots: S;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type SectionLabelVariants } from "../../lib/ui.js";
|
|
2
|
+
import { type RowDensity } from "./row.js";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
label?: string;
|
|
5
|
+
tone?: NonNullable<SectionLabelVariants[`tone`]>;
|
|
6
|
+
heading?: 2 | 3 | 4;
|
|
7
|
+
density?: RowDensity;
|
|
8
|
+
};
|
|
9
|
+
declare var __VLS_1: {}, __VLS_9: {}, __VLS_11: {}, __VLS_13: {};
|
|
10
|
+
type __VLS_Slots = {} & {
|
|
11
|
+
mark?: (props: typeof __VLS_1) => any;
|
|
12
|
+
} & {
|
|
13
|
+
label?: (props: typeof __VLS_9) => any;
|
|
14
|
+
} & {
|
|
15
|
+
info?: (props: typeof __VLS_11) => any;
|
|
16
|
+
} & {
|
|
17
|
+
actions?: (props: typeof __VLS_13) => any;
|
|
18
|
+
};
|
|
19
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
21
|
+
declare const _default: typeof __VLS_export;
|
|
22
|
+
export default _default;
|
|
23
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
24
|
+
new (): {
|
|
25
|
+
$slots: S;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -11,6 +11,12 @@ export declare const ROW_TIERS: {
|
|
|
11
11
|
readonly description: "text-xs";
|
|
12
12
|
readonly headline: "basis-56";
|
|
13
13
|
readonly mark: 28;
|
|
14
|
+
readonly heading: "sm";
|
|
15
|
+
readonly headingLine: "gap-x-2.5 gap-y-1";
|
|
16
|
+
readonly headingLabel: "min-w-0";
|
|
17
|
+
readonly headingInset: "px-1";
|
|
18
|
+
readonly headingGap: "mb-2.5";
|
|
19
|
+
readonly floor: "md:*:min-h-16";
|
|
14
20
|
};
|
|
15
21
|
readonly compact: {
|
|
16
22
|
readonly pad: "px-4 py-2.5";
|
|
@@ -21,6 +27,12 @@ export declare const ROW_TIERS: {
|
|
|
21
27
|
readonly description: "text-2xs";
|
|
22
28
|
readonly headline: "basis-44";
|
|
23
29
|
readonly mark: 22;
|
|
30
|
+
readonly heading: "sm";
|
|
31
|
+
readonly headingLine: "gap-x-2.5 gap-y-1";
|
|
32
|
+
readonly headingLabel: "min-w-0";
|
|
33
|
+
readonly headingInset: "px-1";
|
|
34
|
+
readonly headingGap: "mb-2.5";
|
|
35
|
+
readonly floor: "md:*:min-h-14";
|
|
24
36
|
};
|
|
25
37
|
readonly dense: {
|
|
26
38
|
readonly pad: "px-2.5 py-2";
|
|
@@ -31,6 +43,12 @@ export declare const ROW_TIERS: {
|
|
|
31
43
|
readonly description: "text-2xs";
|
|
32
44
|
readonly headline: "basis-24";
|
|
33
45
|
readonly mark: 18;
|
|
46
|
+
readonly heading: "xs";
|
|
47
|
+
readonly headingLine: "gap-x-2 gap-y-0.5";
|
|
48
|
+
readonly headingLabel: "min-w-0 truncate";
|
|
49
|
+
readonly headingInset: "pl-2 pr-1";
|
|
50
|
+
readonly headingGap: "mb-1";
|
|
51
|
+
readonly floor: "md:*:min-h-12";
|
|
34
52
|
};
|
|
35
53
|
};
|
|
36
54
|
export declare const ROW_TONES: Record<RowTone, string>;
|
|
@@ -32,6 +32,7 @@ export { type DagEdge, type DagNode } from "./components/charts/dagLayout.js";
|
|
|
32
32
|
export { default as FilterBar } from "./components/forms/FilterBar.vue";
|
|
33
33
|
export { default as Icon } from "./components/primitives/Icon.vue";
|
|
34
34
|
export { default as ImageView } from "./components/primitives/ImageView.vue";
|
|
35
|
+
export { default as InlineRename } from "./components/forms/InlineRename.vue";
|
|
35
36
|
export { default as InfoDialog } from "./components/overlays/InfoDialog.vue";
|
|
36
37
|
export { default as InfoHint } from "./components/feedback/InfoHint.vue";
|
|
37
38
|
export { default as InfoTable } from "./components/feedback/InfoTable.vue";
|
|
@@ -66,10 +67,14 @@ export { default as ResizeSeam } from "./components/layout/ResizeSeam.vue";
|
|
|
66
67
|
export { default as ResponsiveOverlay } from "./components/overlays/ResponsiveOverlay.vue";
|
|
67
68
|
export { default as Row } from "./components/rows/Row.vue";
|
|
68
69
|
export { default as RowGroup } from "./components/rows/RowGroup.vue";
|
|
70
|
+
export { default as Section } from "./components/rows/Section.vue";
|
|
71
|
+
export { default as SectionHeader } from "./components/rows/SectionHeader.vue";
|
|
72
|
+
export { default as RowNote } from "./components/rows/RowNote.vue";
|
|
69
73
|
export { default as SearchBar } from "./components/forms/SearchBar.vue";
|
|
70
74
|
export { default as SegmentedControl } from "./components/forms/SegmentedControl.vue";
|
|
71
75
|
export { seriesColor } from "./components/charts/seriesAccent.js";
|
|
72
76
|
export { default as SkeletonRows } from "./components/feedback/SkeletonRows.vue";
|
|
77
|
+
export { default as Await } from "./components/feedback/Await.vue";
|
|
73
78
|
export { default as SkeletonSnapshot } from "./components/feedback/SkeletonSnapshot.vue";
|
|
74
79
|
export { vSkeletonSource } from "./lib/skeleton/skeletonSource.js";
|
|
75
80
|
export { default as SplitView } from "./components/layout/SplitView.vue";
|
package/names.d.mts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const extensionUiNames: readonly string[];
|
|
2
|
+
|
|
3
|
+
export interface ExtensionUiDeprecation {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly use: string;
|
|
6
|
+
/** The extension API version an author first reads it as deprecated in. */
|
|
7
|
+
readonly since: string;
|
|
8
|
+
readonly deprecated: string;
|
|
9
|
+
readonly removableIn: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export declare const extensionUiDeprecated: readonly ExtensionUiDeprecation[];
|
|
13
|
+
|
|
14
|
+
/** A prop (`Component.prop`) or recipe member (`ui.member`) a view may no longer use, though every name still links. */
|
|
15
|
+
export interface ExtensionUiRetirement {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly use: string;
|
|
18
|
+
readonly since: string;
|
|
19
|
+
readonly why: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export declare const extensionUiRetired: readonly ExtensionUiRetirement[];
|
package/names.mjs
CHANGED
|
@@ -75,6 +75,7 @@ export const extensionUiNames = [
|
|
|
75
75
|
"InfoDialog",
|
|
76
76
|
"InfoHint",
|
|
77
77
|
"InfoTable",
|
|
78
|
+
"InlineRename",
|
|
78
79
|
"Markdown",
|
|
79
80
|
"MarkdownDocument",
|
|
80
81
|
"MarkdownFigure",
|
|
@@ -102,6 +103,9 @@ export const extensionUiNames = [
|
|
|
102
103
|
"ResponsiveOverlay",
|
|
103
104
|
"Row",
|
|
104
105
|
"RowGroup",
|
|
106
|
+
"RowNote",
|
|
107
|
+
"Section",
|
|
108
|
+
"SectionHeader",
|
|
105
109
|
"ScrollFrame",
|
|
106
110
|
"SearchBar",
|
|
107
111
|
"SegmentedControl",
|
|
@@ -109,6 +113,7 @@ export const extensionUiNames = [
|
|
|
109
113
|
"similarity",
|
|
110
114
|
"sinceOf",
|
|
111
115
|
"SkeletonRows",
|
|
116
|
+
"Await",
|
|
112
117
|
"SkeletonSnapshot",
|
|
113
118
|
"vSkeletonSource",
|
|
114
119
|
"SplitView",
|
|
@@ -140,13 +145,16 @@ export const extensionUiNames = [
|
|
|
140
145
|
"wordDiff",
|
|
141
146
|
];
|
|
142
147
|
|
|
143
|
-
// Names kept only so extensions built against them still link: what to use instead, when it was deprecated,
|
|
144
|
-
//
|
|
145
|
-
//
|
|
148
|
+
// Names kept only so extensions built against them still link: what to use instead, when it was deprecated, the
|
|
149
|
+
// extension API version an author first reads it as deprecated in (`since`, recorded per version in extension-api's
|
|
150
|
+
// surface.json as `uiKitDeprecated`), and the first version that may drop it. A name leaves the kit only from this
|
|
151
|
+
// list, and only in a major at or past its floor; the entry stays after it goes, as the record that its going was
|
|
152
|
+
// declared. `intentic-kit-check` (check/cli.mjs) refuses an extension importing one.
|
|
146
153
|
export const extensionUiDeprecated = [
|
|
147
154
|
{
|
|
148
155
|
name: "errorMessage",
|
|
149
156
|
use: "messageOr",
|
|
157
|
+
since: "2.31.0",
|
|
150
158
|
deprecated: "2026-10-06",
|
|
151
159
|
removableIn: "3.0.0",
|
|
152
160
|
},
|
|
@@ -154,7 +162,69 @@ export const extensionUiDeprecated = [
|
|
|
154
162
|
// The repository column RepoPicker replaced on 2026-10-12, removed that day with no version moving and restored.
|
|
155
163
|
name: "RepoRail",
|
|
156
164
|
use: "RepoPicker",
|
|
165
|
+
since: "2.31.0",
|
|
157
166
|
deprecated: "2026-10-15",
|
|
158
167
|
removableIn: "3.0.0",
|
|
159
168
|
},
|
|
169
|
+
{
|
|
170
|
+
// A view wired its own wait from these three, and each one it got wrong was a skeleton for ever, none at all, or
|
|
171
|
+
// the wrong one. <Await :of="loaded" imprint="…"> owns the reveal, the remembered look and what it is taken from.
|
|
172
|
+
name: "SkeletonSnapshot",
|
|
173
|
+
use: "Await",
|
|
174
|
+
since: "2.33.0",
|
|
175
|
+
deprecated: "2026-10-20",
|
|
176
|
+
removableIn: "3.0.0",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "vSkeletonSource",
|
|
180
|
+
use: "Await",
|
|
181
|
+
since: "2.33.0",
|
|
182
|
+
deprecated: "2026-10-20",
|
|
183
|
+
removableIn: "3.0.0",
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: "useLoadingReveal",
|
|
187
|
+
use: "Await",
|
|
188
|
+
since: "2.33.0",
|
|
189
|
+
deprecated: "2026-10-20",
|
|
190
|
+
removableIn: "3.0.0",
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
// What a view may no longer decide at a call site although the name it is on still links: a component's prop that was
|
|
195
|
+
// removed (an older bundle passing it has it ignored, as a stray attribute) and a recipe drawn by hand where a component
|
|
196
|
+
// now owns the look. `since` is the extension API version an author first reads it in (surface.json's
|
|
197
|
+
// `uiKitDeprecated`); `why` is what the kit's own checks say where a call site still does it (check/kit-decisions.mjs
|
|
198
|
+
// for props, check/extension.mjs for members), so the reason is written once.
|
|
199
|
+
export const extensionUiRetired = [
|
|
200
|
+
{
|
|
201
|
+
name: "RowGroup.count",
|
|
202
|
+
use: "the rows themselves",
|
|
203
|
+
since: "2.34.0",
|
|
204
|
+
why: "the rows below are the tally, and a count beside a header is what a dozen views had removed one at a time",
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "RowGroup.caption",
|
|
208
|
+
use: "RowNote",
|
|
209
|
+
since: "2.34.0",
|
|
210
|
+
why: "words beside a header were removed view by view as useless; a sentence a reader needs is a <RowNote> on the group's surface",
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: "RowGroup.flat",
|
|
214
|
+
use: "Section",
|
|
215
|
+
since: "2.34.0",
|
|
216
|
+
why: "a group draws no card on a surface (a dialog, a <Card>, another group's card, an overlay) by itself; a label over things that are not rows is a <Section>",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "NavRail.stickyHeadings",
|
|
220
|
+
use: "nothing: a rail's headers scroll with it",
|
|
221
|
+
since: "2.34.0",
|
|
222
|
+
why: "a pinned header needs a fill to scroll under, and that band is the dark box the documentation rail was fixed for three times",
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "ui.sectionLabel",
|
|
226
|
+
use: "Section, or SectionHeader for a header alone",
|
|
227
|
+
since: "2.34.0",
|
|
228
|
+
why: "a header drawn in the section label's look by hand leaves its size, spacing and what sits beside it to the view, where they drift; <Section :label> (or <RowGroup :label> over rows) draws the kit's one header",
|
|
229
|
+
},
|
|
160
230
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentic/extension-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.346.0",
|
|
4
4
|
"description": "The UI kit intentic extensions render with, host-provided at runtime (the app's own components via the import map), so extension UI is always native to the shell",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
14
|
"src",
|
|
15
|
-
"names.mjs"
|
|
15
|
+
"names.mjs",
|
|
16
|
+
"names.d.mts",
|
|
17
|
+
"check"
|
|
16
18
|
],
|
|
17
19
|
"publishConfig": {
|
|
18
20
|
"access": "public",
|
|
@@ -20,6 +22,9 @@
|
|
|
20
22
|
},
|
|
21
23
|
"main": "./dist/index.js",
|
|
22
24
|
"types": "./dist/types/extension-ui/src/index.d.ts",
|
|
25
|
+
"bin": {
|
|
26
|
+
"intentic-kit-check": "./check/cli.mjs"
|
|
27
|
+
},
|
|
23
28
|
"exports": {
|
|
24
29
|
".": {
|
|
25
30
|
"@intentic/src": "./src/index.ts",
|
|
@@ -51,7 +56,8 @@
|
|
|
51
56
|
"types": "./dist/types/extension-ui/src/i18n.d.ts",
|
|
52
57
|
"default": "./dist/i18n.js"
|
|
53
58
|
},
|
|
54
|
-
"./names": "./names.mjs"
|
|
59
|
+
"./names": "./names.mjs",
|
|
60
|
+
"./check": "./check/extension.mjs"
|
|
55
61
|
},
|
|
56
62
|
"dependencies": {
|
|
57
63
|
"primevue": "4.5.5"
|
package/src/index.ts
CHANGED
|
@@ -81,6 +81,9 @@ export {
|
|
|
81
81
|
InfoDialog,
|
|
82
82
|
InfoHint,
|
|
83
83
|
InfoTable,
|
|
84
|
+
// The app's one rename: the name in place with its pencil, the field in the same box, and its failure; never a
|
|
85
|
+
// field of an extension's own beside the name.
|
|
86
|
+
InlineRename,
|
|
84
87
|
// What a file is by its name (category, label, whether it is ever text), the table the shell's own surfaces read.
|
|
85
88
|
type FileFormat,
|
|
86
89
|
formatOf,
|
|
@@ -139,19 +142,27 @@ export {
|
|
|
139
142
|
// Use over `AnchoredOverlay` for a menu: anchored on desktop, a thumb-reachable sheet on a phone, one open flag.
|
|
140
143
|
ResponsiveOverlay,
|
|
141
144
|
Row,
|
|
145
|
+
// A list is a <RowGroup>: the one section header over the rows' card, on which <RowNote> says what is none of its
|
|
146
|
+
// rows. A label over things that are not rows is a <Section>; the header alone (a panel's title) is
|
|
147
|
+
// <SectionHeader>. None of them can carry a count, a caption or a fill, and their spacing is the tier's.
|
|
142
148
|
RowGroup,
|
|
149
|
+
RowNote,
|
|
150
|
+
Section,
|
|
151
|
+
SectionHeader,
|
|
143
152
|
SearchBar,
|
|
144
153
|
SegmentedControl,
|
|
145
154
|
seriesColor,
|
|
146
155
|
sinceOf,
|
|
147
|
-
//
|
|
148
|
-
// thresholds.
|
|
156
|
+
// Deprecated (2026-10-20) with SkeletonSnapshot and vSkeletonSource: <Await> holds all three.
|
|
149
157
|
useLoadingReveal,
|
|
150
158
|
// Renders real `<Row>`s rather than divs, so its height matches what replaces it and the page doesn't jump.
|
|
151
159
|
SkeletonRows,
|
|
152
|
-
// A
|
|
153
|
-
//
|
|
154
|
-
// Prefix the name with the extension's id, since every
|
|
160
|
+
// A region's wait: `<Await :of="query.loaded" imprint="ext-id.list">`, where `loaded` is what `api.sandbox.query`
|
|
161
|
+
// returns. Nothing for a moment, then the region as it last looked in this sandbox (its `#fallback` until seen once),
|
|
162
|
+
// the answer in the default slot, or the failure in words. Prefix the name with the extension's id, since every
|
|
163
|
+
// extension's imprints share the sandbox's store. A view's first wait is its host's: every read joins it.
|
|
164
|
+
Await,
|
|
165
|
+
// Deprecated (2026-10-20): <Await>'s insides, kept so extensions built against them still link.
|
|
155
166
|
SkeletonSnapshot,
|
|
156
167
|
vSkeletonSource,
|
|
157
168
|
SplitView,
|