diff --git a/.dev/imported/react-icon-browser/.dependency-cruiser.cjs b/.dependency-cruiser.cjs
similarity index 73%
rename from .dev/imported/react-icon-browser/.dependency-cruiser.cjs
rename to .dependency-cruiser.cjs
index 0fe4990..26cc908 100644
--- a/.dev/imported/react-icon-browser/.dependency-cruiser.cjs
+++ b/.dependency-cruiser.cjs
@@ -27,9 +27,9 @@
* hand-written exception list did not know about the new file.
*/
// src/app/ is the composition root architecture.md 3.2 names (the browser() builder first).
-const APP = '^src/(App|main|mount|embedded|IchavaBrowser)\\.tsx$|^src/app/';
+const APP = '^resources/js/(StandaloneApp|app|main|mount|embedded|IchavaBrowser)\\.tsx$|^resources/js/(app|pages)/';
/** The package barrel re-exports the public surface, entry points included. */
-const BARREL = '^src/index\\.ts$';
+const BARREL = '^resources/js/index\\.ts$';
module.exports = {
forbidden: [
@@ -40,7 +40,7 @@ module.exports = {
'return type puts React into the public surface a Vue or Blade consumer inherits. ' +
'Use the plain StyleObject from core/types instead, and put the hook in hooks/.',
severity: 'error',
- from: { path: '^src/core/' },
+ from: { path: '^resources/js/core/' },
// NOTE: `to.path` matches the RESOLVED path, not the import specifier, so this must
// be written against node_modules. Written as '^react$' the rule silently never
// fires -- which is how it shipped the first time, and why every rule here has a
@@ -53,8 +53,12 @@ module.exports = {
'core/ must not depend on any other layer. Test fixtures are the one exception: ' +
'src/test/ is shared scaffolding, not a layer.',
severity: 'error',
- from: { path: '^src/core/', pathNot: '\\.test\\.tsx?$' },
- to: { path: `^src/(components|hooks|store\\.ts)|${APP}` },
+ // Two exemptions, measured when these rules came over from react-icon-browser (react
+ // consolidation 3.3, 2026-11-13): the Inertia adapter's InertiaCatalog.ts and browserMount.ts
+ // predate the merge and import the query helpers in hooks/useFilterSync.ts. A third file
+ // that does the same fails.
+ from: { path: '^resources/js/core/', pathNot: ['\\.test\\.tsx?$', '^resources/js/core/(InertiaCatalog|browserMount)\\.ts$'] },
+ to: { path: `^resources/js/(components|hooks|store\\.ts)|${APP}` },
},
{
name: 'core-tests-stay-in-core',
@@ -62,14 +66,14 @@ module.exports = {
'A test under core/ that imports a component is testing the wrong layer -- move the ' +
'test next to what it covers. src/test/ fixtures are allowed.',
severity: 'error',
- from: { path: '^src/core/.*\\.test\\.tsx?$' },
- to: { path: '^src/(components|hooks)/' },
+ from: { path: '^resources/js/core/.*\\.test\\.tsx?$' },
+ to: { path: '^resources/js/(components|hooks)/' },
},
{
name: 'data-no-react',
comment: 'The store and data layer must stay portable: no React imports.',
severity: 'error',
- from: { path: '^src/(store\\.ts|data/)' },
+ from: { path: '^resources/js/(store\\.ts|data/)' },
// NOTE: `to.path` matches the RESOLVED path, not the import specifier, so this must
// be written against node_modules. Written as '^react$' the rule silently never
// fires -- which is how it shipped the first time, and why every rule here has a
@@ -80,7 +84,7 @@ module.exports = {
name: 'ui-not-into-app',
comment: 'Components take props; the app wires them. ui/ must not reach into app/.',
severity: 'error',
- from: { path: '^src/(components|hooks)/' },
+ from: { path: '^resources/js/(components|hooks)/' },
to: { path: APP },
},
{
@@ -111,31 +115,31 @@ module.exports = {
'A module domain imports no framework: no React, zustand, react-aria or Untitled UI, ' +
'type-only imports included. The domain tsconfig drops DOM from lib for the same reason.',
severity: 'error',
- from: { path: '^src/modules/[^/]+/domain/' },
+ from: { path: '^resources/js/modules/[^/]+/domain/' },
to: { path: '^node_modules/(react|react-dom|zustand|react-aria|react-aria-components|@react-aria|@react-stately|@react-types|@untitledui)(/|$)' },
},
{
name: 'module-domain-is-innermost',
comment: 'A domain depends on nothing outward: not its own application or infrastructure, not ui, state or the app.',
severity: 'error',
- from: { path: '^src/modules/[^/]+/domain/' },
- to: { path: `^src/(components|hooks|state|ui|app)/|^src/store\\.ts$|^src/modules/[^/]+/(application|infrastructure)/|${APP}` },
+ from: { path: '^resources/js/modules/[^/]+/domain/' },
+ to: { path: `^resources/js/(components|hooks|state|ui|app)/|^resources/js/store\\.ts$|^resources/js/modules/[^/]+/(application|infrastructure)/|${APP}` },
},
{
name: 'module-application-not-outward',
comment: 'Application services use the domain and ports; adapters, ui, state and React stay outside.',
severity: 'error',
- from: { path: '^src/modules/[^/]+/application/' },
+ from: { path: '^resources/js/modules/[^/]+/application/' },
to: {
- path: `^src/(components|hooks|state|ui|app)/|^src/store\\.ts$|^src/modules/[^/]+/infrastructure/|${APP}|^node_modules/(react|react-dom|zustand)(/|$)`,
+ path: `^resources/js/(components|hooks|state|ui|app)/|^resources/js/store\\.ts$|^resources/js/modules/[^/]+/infrastructure/|${APP}|^node_modules/(react|react-dom|zustand)(/|$)`,
},
},
{
name: 'module-infrastructure-not-into-ui',
comment: 'Adapters implement ports; they never reach ui, state, the app or React.',
severity: 'error',
- from: { path: '^src/modules/[^/]+/infrastructure/' },
- to: { path: `^src/(components|hooks|state|ui|app)/|^src/store\\.ts$|${APP}|^node_modules/(react|react-dom)(/|$)` },
+ from: { path: '^resources/js/modules/[^/]+/infrastructure/' },
+ to: { path: `^resources/js/(components|hooks|state|ui|app)/|^resources/js/store\\.ts$|${APP}|^node_modules/(react|react-dom)(/|$)` },
},
{
name: 'modules-meet-through-index',
@@ -143,15 +147,15 @@ module.exports = {
"One module reaches another only through that module's index.ts, so a module's " +
'internals can move without breaking its neighbours.',
severity: 'error',
- from: { path: '^src/modules/([^/]+)/' },
- to: { path: '^src/modules/[^/]+/', pathNot: ['^src/modules/$1/', '^src/modules/[^/]+/index\\.ts$'] },
+ from: { path: '^resources/js/modules/([^/]+)/' },
+ to: { path: '^resources/js/modules/[^/]+/', pathNot: ['^resources/js/modules/$1/', '^resources/js/modules/[^/]+/index\\.ts$'] },
},
{
name: 'state-not-into-ui',
comment: 'The store slices hold state and call domain and application code; they never import ui.',
severity: 'error',
- from: { path: '^src/state/' },
- to: { path: `^src/(components|hooks|ui)/|${APP}|^node_modules/(react|react-dom)(/|$)` },
+ from: { path: '^resources/js/state/' },
+ to: { path: `^resources/js/(components|hooks|ui)/|${APP}|^node_modules/(react|react-dom)(/|$)` },
},
{
name: 'no-circular',
@@ -172,7 +176,7 @@ module.exports = {
'(^|/)\\.[^/]+\\.(cjs|mjs|js|ts)$',
'\\.d\\.ts$',
'(^|/)tsconfig\\.[^/]+\\.json$',
- '^src/(main|mount|embedded)\\.tsx$',
+ '^resources/js/(main|mount|embedded|app)\\.tsx$',
],
},
to: {},
@@ -184,10 +188,12 @@ module.exports = {
'no-orphans only sees modules with no edges at all, so a dead file that imported ' +
'clsx (src/lib/utils.ts) passed it. Tests, declaration files and configs are exempt.',
severity: 'error',
- from: { path: '^src/(main|mount|embedded)\\.tsx$|^src/index\\.ts$' },
+ // app.tsx is the Inertia entry; it loads pages/ through import.meta.glob, which the
+ // cruiser cannot follow, so each page is an entry point too.
+ from: { path: '^resources/js/(main|mount|embedded|app)\\.tsx$|^resources/js/index\\.ts$|^resources/js/pages/' },
to: {
- path: '^src/',
- pathNot: ['\\.test\\.tsx?$', '^src/test/', '\\.d\\.ts$', '^src/(main|mount|embedded)\\.tsx$', '^src/index\\.ts$'],
+ path: '^resources/js/',
+ pathNot: ['\\.test\\.tsx?$', '^resources/js/test/', '\\.d\\.ts$', '^resources/js/(main|mount|embedded|app)\\.tsx$', '^resources/js/index\\.ts$', '^resources/js/pages/'],
reachable: false,
},
},
@@ -196,7 +202,7 @@ module.exports = {
options: {
doNotFollow: { path: 'node_modules' },
tsPreCompilationDeps: true, // so type-only imports are seen -- the whole point of core-no-react
- tsConfig: { fileName: 'tsconfig.app.json' },
+ tsConfig: { fileName: 'tsconfig.inertia.json' },
enhancedResolveOptions: { exportsFields: ['exports'], conditionNames: ['import', 'require', 'types'] },
reporterOptions: { text: { highlightFocused: true } },
},
diff --git a/.dev/browser/check-boundary-rules.mjs b/.dev/browser/check-boundary-rules.mjs
index 2e76097..7ac9e4a 100644
--- a/.dev/browser/check-boundary-rules.mjs
+++ b/.dev/browser/check-boundary-rules.mjs
@@ -1,7 +1,7 @@
#!/usr/bin/env node
// Proves the module boundary rules in .dependency-cruiser.cjs can fail.
//
-// The rules for src/modules/ and src/state/ land before the code they govern
+// The rules for resources/js/modules/ and resources/js/state/ land before the code they govern
// (architecture.md 4.1), so on a tree with no modules they match nothing, and "no
// violations" would say nothing about whether they work. `core-no-react` once shipped
// matching nothing for exactly that reason.
@@ -16,10 +16,11 @@ import { mkdirSync, rmdirSync, rmSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
-const root = join(dirname(fileURLToPath(import.meta.url)), '..');
-const A = 'src/modules/__probe_a__';
-const B = 'src/modules/__probe_b__';
-const STATE = 'src/state/__probe__.ts';
+// .dev/browser/ -> the repository root (react consolidation 3.3, 2026-11-13).
+const root = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
+const A = 'resources/js/modules/__probe_a__';
+const B = 'resources/js/modules/__probe_b__';
+const STATE = 'resources/js/state/__probe__.ts';
const PLANTED = [A, B, STATE];
/** file -> source. Each import names an existing module or another plant. */
@@ -48,7 +49,7 @@ const CONTROL = `${B}/domain/through-index.ts`;
function cleanup() {
for (const p of PLANTED) rmSync(join(root, p), { recursive: true, force: true });
// Remove the parent directories too when the plants were all they held.
- for (const dir of ['src/modules', 'src/state']) {
+ for (const dir of ['resources/js/modules', 'resources/js/state']) {
try {
rmdirSync(join(root, dir));
} catch {
diff --git a/.dev/browser/check-dead-classes.mjs b/.dev/browser/check-dead-classes.mjs
index 20df0ef..91a1125 100644
--- a/.dev/browser/check-dead-classes.mjs
+++ b/.dev/browser/check-dead-classes.mjs
@@ -15,7 +15,10 @@
* source that was meant to produce it. Grepping the Tailwind config or the component
* proves how the class was written, never what the compiler kept.
*
- * Run after `npm run build`, against `dist/`.
+ * Run after `npm run build:prod`, against the committed bundle's stylesheet in public/assets/css/,
+ * which is what a host receives. Moved from react-icon-browser's scripts/ (react consolidation 3.3,
+ * 2026-11-13): it read that repository's dist/assets and src/; the standalone harness pages it
+ * also scanned now live in e2e/browser/.
*
* WHAT IT DOES NOT COVER, so nobody reads a pass as more than it is:
*
@@ -32,9 +35,10 @@ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
import { join, extname, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
-const ROOT = fileURLToPath(new URL('..', import.meta.url));
-const CSS_DIR = join(ROOT, 'dist/assets');
-const SRC_DIR = join(ROOT, 'src');
+const ROOT = fileURLToPath(new URL('../..', import.meta.url));
+const CSS_DIR = join(ROOT, 'public/assets/css');
+const SRC_DIR = join(ROOT, 'resources/js');
+const HTML_DIR = join(ROOT, 'e2e/browser');
/**
* Class names that are expected to emit nothing, each with the reason it is not a defect.
@@ -84,9 +88,9 @@ function collectTokens() {
};
walk(SRC_DIR).forEach(scan);
- readdirSync(ROOT)
+ readdirSync(HTML_DIR)
.filter((f) => f.endsWith('.html'))
- .forEach((f) => scan(join(ROOT, f)));
+ .forEach((f) => scan(join(HTML_DIR, f)));
return tokens;
}
@@ -98,11 +102,11 @@ function collectTokens() {
*/
function loadCss() {
if (!existsSync(CSS_DIR)) {
- fail(`No ${relative(ROOT, CSS_DIR)} directory. Run \`npm run build\` first.`);
+ fail(`No ${relative(ROOT, CSS_DIR)} directory. Run \`npm run build:prod\` first.`);
}
const files = readdirSync(CSS_DIR).filter((f) => f.endsWith('.css'));
if (files.length === 0) {
- fail(`No CSS in ${relative(ROOT, CSS_DIR)}. Run \`npm run build\` first.`);
+ fail(`No CSS in ${relative(ROOT, CSS_DIR)}. Run \`npm run build:prod\` first.`);
}
const css = files.map((f) => readFileSync(join(CSS_DIR, f), 'utf8')).join('\n');
return { css: css.replace(/\\/g, ''), files };
diff --git a/.dev/browser/check-playwright-image.mjs b/.dev/browser/check-playwright-image.mjs
index 8bb027d..69967a0 100644
--- a/.dev/browser/check-playwright-image.mjs
+++ b/.dev/browser/check-playwright-image.mjs
@@ -22,13 +22,14 @@ import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
-const root = join(dirname(fileURLToPath(import.meta.url)), '..');
+// .dev/browser/ -> the repository root (react consolidation 3.3, 2026-11-13).
+const root = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
const IMAGE = /mcr\.microsoft\.com\/playwright:(v[\d.]+-\w+)/g;
const sources = [
- ['.github/workflows/ci.yml', 'CI visual job container'],
+ ['.github/workflows/browser.yml', 'CI visual job container'],
['.github/workflows/visual-baselines.yml', 'seed workflow container'],
- ['scripts/linux.sh', 'npm run visual:linux, visual:linux:check and e2e:linux'],
+ ['.dev/browser/linux.sh', 'npm run visual:linux, visual:linux:check and e2e:linux'],
];
const found = [];
diff --git a/.dev/browser/linux.sh b/.dev/browser/linux.sh
index 919fa63..82ea425 100644
--- a/.dev/browser/linux.sh
+++ b/.dev/browser/linux.sh
@@ -7,17 +7,17 @@
# npm run e2e:linux run the behavioural E2E suite
#
# Needs Docker. Arguments after the mode go to `playwright test` (a spec file, --grep ...).
-# The image is pinned here once; check:playwright-image fails if it drifts from ci.yml's
+# The image is pinned here once; check:playwright-image fails if it drifts from browser.yml's
# visual job, the seed workflow or @playwright/test.
set -eu
IMAGE=mcr.microsoft.com/playwright:v1.65.0-noble
-mode=${1:?usage: scripts/linux.sh visual-update|visual-check|e2e [playwright args]}
+mode=${1:?usage: .dev/browser/linux.sh visual-update|visual-check|e2e [playwright args]}
shift
if ! command -v docker >/dev/null 2>&1; then
- echo "scripts/linux.sh: Docker is not installed or not on PATH; CI's image needs it." >&2
+ echo ".dev/browser/linux.sh: Docker is not installed or not on PATH; CI's image needs it." >&2
exit 127
fi
-exec docker run --rm --ipc=host -v "$PWD":/src "$IMAGE" sh /src/scripts/visual-linux.sh "$mode" "$@"
+exec docker run --rm --ipc=host -v "$PWD":/src "$IMAGE" sh /src/.dev/browser/visual-linux.sh "$mode" "$@"
diff --git a/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__ax.svg b/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__ax.svg
new file mode 100644
index 0000000..cd4e76d
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__ax.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__ba.svg b/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__ba.svg
new file mode 100644
index 0000000..1f84409
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__ba.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__bi.svg b/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__bi.svg
new file mode 100644
index 0000000..96fac20
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__clip_or_mask__bi.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__defs_ids__ag.svg b/.dev/browser/public/baseline/icon-sets-flag__defs_ids__ag.svg
new file mode 100644
index 0000000..f25ccea
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__defs_ids__ag.svg
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__defs_ids__ai.svg b/.dev/browser/public/baseline/icon-sets-flag__defs_ids__ai.svg
new file mode 100644
index 0000000..6684777
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__defs_ids__ai.svg
@@ -0,0 +1,29 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__defs_ids__aw.svg b/.dev/browser/public/baseline/icon-sets-flag__defs_ids__aw.svg
new file mode 100644
index 0000000..a87801e
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__defs_ids__aw.svg
@@ -0,0 +1,186 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__gradient__bz.svg b/.dev/browser/public/baseline/icon-sets-flag__gradient__bz.svg
new file mode 100644
index 0000000..dbaf398
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__gradient__bz.svg
@@ -0,0 +1,145 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__gradient__fk.svg b/.dev/browser/public/baseline/icon-sets-flag__gradient__fk.svg
new file mode 100644
index 0000000..1ab36f9
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__gradient__fk.svg
@@ -0,0 +1,89 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__gradient__gs.svg b/.dev/browser/public/baseline/icon-sets-flag__gradient__gs.svg
new file mode 100644
index 0000000..2453be3
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__gradient__gs.svg
@@ -0,0 +1,132 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__own_colour__ae.svg b/.dev/browser/public/baseline/icon-sets-flag__own_colour__ae.svg
new file mode 100644
index 0000000..a6e369b
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__own_colour__ae.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__own_colour__am.svg b/.dev/browser/public/baseline/icon-sets-flag__own_colour__am.svg
new file mode 100644
index 0000000..676d2ff
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__own_colour__am.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__own_colour__ao.svg b/.dev/browser/public/baseline/icon-sets-flag__own_colour__ao.svg
new file mode 100644
index 0000000..d91a3eb
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__own_colour__ao.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__use_ref__ad.svg b/.dev/browser/public/baseline/icon-sets-flag__use_ref__ad.svg
new file mode 100644
index 0000000..dde82d2
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__use_ref__ad.svg
@@ -0,0 +1,148 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__use_ref__af.svg b/.dev/browser/public/baseline/icon-sets-flag__use_ref__af.svg
new file mode 100644
index 0000000..e1614f3
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__use_ref__af.svg
@@ -0,0 +1,81 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-flag__use_ref__al.svg b/.dev/browser/public/baseline/icon-sets-flag__use_ref__al.svg
new file mode 100644
index 0000000..4abb979
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-flag__use_ref__al.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-tabler__current_color__accessible.svg b/.dev/browser/public/baseline/icon-sets-tabler__current_color__accessible.svg
new file mode 100644
index 0000000..4b919b3
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-tabler__current_color__accessible.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-tabler__current_color__ad-circle.svg b/.dev/browser/public/baseline/icon-sets-tabler__current_color__ad-circle.svg
new file mode 100644
index 0000000..e328abf
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-tabler__current_color__ad-circle.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/icon-sets-tabler__current_color__ad.svg b/.dev/browser/public/baseline/icon-sets-tabler__current_color__ad.svg
new file mode 100644
index 0000000..18c9b65
--- /dev/null
+++ b/.dev/browser/public/baseline/icon-sets-tabler__current_color__ad.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/.dev/browser/public/baseline/manifest.json b/.dev/browser/public/baseline/manifest.json
new file mode 100644
index 0000000..67fb8bc
--- /dev/null
+++ b/.dev/browser/public/baseline/manifest.json
@@ -0,0 +1,110 @@
+[
+ {
+ "pack": "icon-sets-tabler",
+ "feature": "current_color",
+ "file": "icon-sets-tabler__current_color__accessible.svg",
+ "bytes": 746
+ },
+ {
+ "pack": "icon-sets-tabler",
+ "feature": "current_color",
+ "file": "icon-sets-tabler__current_color__ad-circle.svg",
+ "bytes": 794
+ },
+ {
+ "pack": "icon-sets-tabler",
+ "feature": "current_color",
+ "file": "icon-sets-tabler__current_color__ad.svg",
+ "bytes": 787
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "defs_ids",
+ "file": "icon-sets-flag__defs_ids__ag.svg",
+ "bytes": 750
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "defs_ids",
+ "file": "icon-sets-flag__defs_ids__ai.svg",
+ "bytes": 2313
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "defs_ids",
+ "file": "icon-sets-flag__defs_ids__aw.svg",
+ "bytes": 10354
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "use_ref",
+ "file": "icon-sets-flag__use_ref__ad.svg",
+ "bytes": 29141
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "use_ref",
+ "file": "icon-sets-flag__use_ref__af.svg",
+ "bytes": 18983
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "use_ref",
+ "file": "icon-sets-flag__use_ref__al.svg",
+ "bytes": 3138
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "gradient",
+ "file": "icon-sets-flag__gradient__bz.svg",
+ "bytes": 42676
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "gradient",
+ "file": "icon-sets-flag__gradient__fk.svg",
+ "bytes": 28488
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "gradient",
+ "file": "icon-sets-flag__gradient__gs.svg",
+ "bytes": 31921
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "clip_or_mask",
+ "file": "icon-sets-flag__clip_or_mask__ax.svg",
+ "bytes": 542
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "clip_or_mask",
+ "file": "icon-sets-flag__clip_or_mask__ba.svg",
+ "bytes": 1171
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "clip_or_mask",
+ "file": "icon-sets-flag__clip_or_mask__bi.svg",
+ "bytes": 1051
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "own_colour",
+ "file": "icon-sets-flag__own_colour__ae.svg",
+ "bytes": 273
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "own_colour",
+ "file": "icon-sets-flag__own_colour__am.svg",
+ "bytes": 235
+ },
+ {
+ "pack": "icon-sets-flag",
+ "feature": "own_colour",
+ "file": "icon-sets-flag__own_colour__ao.svg",
+ "bytes": 1601
+ }
+]
\ No newline at end of file
diff --git a/.dev/browser/sample-svg-archetypes.mjs b/.dev/browser/sample-svg-archetypes.mjs
index 060f155..336d16a 100644
--- a/.dev/browser/sample-svg-archetypes.mjs
+++ b/.dev/browser/sample-svg-archetypes.mjs
@@ -9,7 +9,7 @@
* Deterministic: directory entries are sorted before sampling, so re-running picks the
* same files and the baseline does not churn.
*
- * Writes to `public/baseline/` plus a manifest the spec reads.
+ * Writes to `.dev/browser/public/baseline/` plus a manifest the spec reads.
*/
import { readdirSync, readFileSync, statSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { join, basename, dirname } from 'node:path';
@@ -20,10 +20,19 @@ import { fileURLToPath } from 'node:url';
// list of directory names that the 2026-09-21 rebrand renamed -- three ways of
// being wrong, none of which anything reported, because the script is run by
// hand and is not wired to an npm script.
-const APP = dirname(dirname(fileURLToPath(import.meta.url)));
+//
+// Moved from react-icon-browser's scripts/ to icon-browser's .dev/browser/ (react consolidation
+// 3.1, 2026-11-13): the repository root is two levels up, the packs sit beside it in packages/,
+// and the sample lands in the e2e harness's public directory.
+//
+// Public packs only. icon-browser is public, and icon-sets-bundled and icon-sets-metronic are
+// private (Metronic "is never shown publicly"), so their samples and contact sheets stay in the
+// retired react-icon-browser and are not reproduced here. The style_block and foreign archetypes
+// came only from those two packs; e2e/browser/svg-baseline.spec.ts says so where it checks.
+const APP = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
const ROOT = dirname(APP);
-const OUT = join(APP, 'public', 'baseline');
-const PACKS = ['icon-sets-tabler', 'icon-sets-bundled', 'icon-sets-metronic', 'icon-sets-flag'];
+const OUT = join(APP, '.dev', 'browser', 'public', 'baseline');
+const PACKS = ['icon-sets-tabler', 'icon-sets-flag'];
const FEATURES = {
style_block: (s) => /