@loworbitstudio/visor 1.18.1 → 1.19.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.4.0",
3
- "generated_at": "2026-07-15T17:44:38.986Z",
3
+ "generated_at": "2026-07-20T05:09:50.016Z",
4
4
  "components": {
5
5
  "accessibility-specimen": {
6
6
  "changeType": "current",
package/dist/index.js CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { readFileSync as readFileSync33 } from "fs";
5
- import { dirname as dirname13, join as join33 } from "path";
5
+ import { dirname as dirname14, join as join33 } from "path";
6
6
  import { fileURLToPath as fileURLToPath4 } from "url";
7
7
  import { Command as Command2 } from "commander";
8
8
 
9
9
  // src/commands/check.ts
10
10
  import { Command } from "commander";
11
11
  import { statSync as statSync3 } from "fs";
12
- import { resolve as resolve4, dirname as dirname2 } from "path";
12
+ import { resolve as resolve4, dirname as dirname3 } from "path";
13
13
 
14
14
  // src/registry/resolve.ts
15
15
  import { readFileSync } from "fs";
@@ -396,7 +396,7 @@ async function scanJsx(pathArg) {
396
396
 
397
397
  // src/check/design.ts
398
398
  import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, existsSync } from "fs";
399
- import { resolve as resolve2, extname as extname2, join as join3, basename } from "path";
399
+ import { resolve as resolve2, extname as extname2, join as join3, basename, dirname as dirname2 } from "path";
400
400
  function loadVisorRc(dir) {
401
401
  const rcPath = join3(dir, ".visorrc.json");
402
402
  if (!existsSync(rcPath)) return {};
@@ -823,6 +823,118 @@ function ruleExcessiveCardNesting(source, filePath) {
823
823
  }
824
824
  return found;
825
825
  }
826
+ var NATIVE_CONTROL_COMPONENTS = /* @__PURE__ */ new Set([
827
+ ...Object.values(NATIVE_TO_VISOR).map((m) => m.visorName),
828
+ ...Object.values(INPUT_TYPE_MAP).map((m) => m.visorName)
829
+ ]);
830
+ var RESET_IMPORT_RE = /@loworbitstudio\/visor-core\/reset/;
831
+ var PREFLIGHT_RE = /@tailwind\s+base|@import\s+["']tailwindcss["']/;
832
+ var baseLayerStateCache = /* @__PURE__ */ new Map();
833
+ var baseLayerReported = /* @__PURE__ */ new Set();
834
+ function resetBaseLayerCache() {
835
+ baseLayerStateCache.clear();
836
+ baseLayerReported.clear();
837
+ }
838
+ function findProjectRoot(filePath) {
839
+ let dir = dirname2(resolve2(filePath));
840
+ for (; ; ) {
841
+ if (existsSync(join3(dir, "package.json"))) return dir;
842
+ const parent = dirname2(dir);
843
+ if (parent === dir) return null;
844
+ dir = parent;
845
+ }
846
+ }
847
+ function collectStylesheets(root) {
848
+ const out = [];
849
+ function recurse(dir, depth) {
850
+ if (depth > 6) return;
851
+ let entries;
852
+ try {
853
+ entries = readdirSync2(dir);
854
+ } catch {
855
+ return;
856
+ }
857
+ for (const entry of entries) {
858
+ if (entry === "node_modules" || entry === "dist" || entry.startsWith(".")) continue;
859
+ const full = join3(dir, entry);
860
+ let s;
861
+ try {
862
+ s = statSync2(full);
863
+ } catch {
864
+ continue;
865
+ }
866
+ if (s.isDirectory()) recurse(full, depth + 1);
867
+ else if (extname2(full) === ".css") out.push(full);
868
+ }
869
+ }
870
+ recurse(root, 0);
871
+ return out;
872
+ }
873
+ function baseLayerState(root) {
874
+ const cached = baseLayerStateCache.get(root);
875
+ if (cached) return cached;
876
+ let hasReset = false;
877
+ let hasPreflight = false;
878
+ for (const sheet of collectStylesheets(root)) {
879
+ let css;
880
+ try {
881
+ css = readFileSync3(sheet, "utf-8");
882
+ } catch {
883
+ continue;
884
+ }
885
+ if (RESET_IMPORT_RE.test(css)) hasReset = true;
886
+ if (PREFLIGHT_RE.test(css)) hasPreflight = true;
887
+ if (hasReset && hasPreflight) break;
888
+ }
889
+ const corePath = join3(root, "node_modules", "@loworbitstudio", "visor-core");
890
+ const state = {
891
+ hasReset,
892
+ hasPreflight,
893
+ visorCoreInstalled: existsSync(corePath),
894
+ visorCoreExportsReset: existsSync(join3(corePath, "dist", "reset.css"))
895
+ };
896
+ baseLayerStateCache.set(root, state);
897
+ return state;
898
+ }
899
+ function ruleMissingVisorBaseLayer(source, filePath) {
900
+ if (!CODE_EXTS.has(extname2(filePath))) return [];
901
+ const src = lines(source);
902
+ let usageLine = -1;
903
+ for (let i = 0; i < src.length; i++) {
904
+ const spec = src[i].match(/from\s+["'][^"']*components\/ui\/([a-z0-9-]+)["']/);
905
+ if (spec && NATIVE_CONTROL_COMPONENTS.has(spec[1])) {
906
+ usageLine = i + 1;
907
+ break;
908
+ }
909
+ }
910
+ if (usageLine === -1) return [];
911
+ const root = findProjectRoot(filePath);
912
+ if (!root || baseLayerReported.has(root)) return [];
913
+ const state = baseLayerState(root);
914
+ if (state.visorCoreInstalled && !state.visorCoreExportsReset) {
915
+ baseLayerReported.add(root);
916
+ return [finding(
917
+ filePath,
918
+ usageLine,
919
+ "missing-visor-base-layer",
920
+ "warn",
921
+ "The installed @loworbitstudio/visor-core predates the /reset export, so Visor's element baseline is unavailable. Form controls will render in the browser's default font rather than your theme font.",
922
+ 'Run `npm update @loworbitstudio/visor-core`, then add `@import "@loworbitstudio/visor-core/reset";` to your global stylesheet.'
923
+ )];
924
+ }
925
+ if (!state.hasReset && !state.hasPreflight) {
926
+ baseLayerReported.add(root);
927
+ return [finding(
928
+ filePath,
929
+ usageLine,
930
+ "missing-visor-base-layer",
931
+ "warn",
932
+ "This project renders Visor form controls but imports neither @loworbitstudio/visor-core/reset nor Tailwind preflight. Without an element baseline, inputs, selects and buttons fall back to the UA font instead of inheriting your theme font.",
933
+ 'Add `@import "@loworbitstudio/visor-core/reset";` to your global stylesheet \u2014 or keep your existing reset/preflight and disable this rule in .visorrc.json.'
934
+ )];
935
+ }
936
+ return [];
937
+ }
826
938
  var RULES = [
827
939
  // Errors — Borealis non-negotiables
828
940
  { name: "tier-1-token-direct-usage", severity: "error", fn: ruleTier1TokenDirectUsage },
@@ -841,11 +953,13 @@ var RULES = [
841
953
  { name: "sub-44px-touch-target", severity: "warn", fn: ruleSub44pxTouchTarget },
842
954
  { name: "line-length-over-75ch", severity: "warn", fn: ruleLineLengthOver75ch },
843
955
  { name: "gradient-text", severity: "warn", fn: ruleGradientText },
844
- { name: "excessive-card-nesting", severity: "warn", fn: ruleExcessiveCardNesting }
956
+ { name: "excessive-card-nesting", severity: "warn", fn: ruleExcessiveCardNesting },
957
+ { name: "missing-visor-base-layer", severity: "warn", fn: ruleMissingVisorBaseLayer }
845
958
  ];
846
959
  function scanDesign(pathArg, options = {}) {
847
960
  const files = collectFiles2(pathArg);
848
961
  const { disabledRules = [], errorsOnly = false } = options;
962
+ resetBaseLayerCache();
849
963
  const activeRules = RULES.filter((r) => {
850
964
  if (disabledRules.includes(r.name)) return false;
851
965
  if (errorsOnly && r.severity !== "error") return false;
@@ -1095,7 +1209,7 @@ async function checkDiffCommand(pathArg, options) {
1095
1209
  }
1096
1210
  function checkDesignCommand(pathArg, options) {
1097
1211
  const absPath = resolve4(pathArg);
1098
- const rcDir = statSync3(absPath).isDirectory() ? absPath : dirname2(absPath);
1212
+ const rcDir = statSync3(absPath).isDirectory() ? absPath : dirname3(absPath);
1099
1213
  const rc = loadVisorRc(rcDir);
1100
1214
  const result = scanDesign(absPath, {
1101
1215
  disabledRules: rc.disabledRules ?? [],
@@ -1204,7 +1318,7 @@ function checkCommand() {
1204
1318
 
1205
1319
  // src/commands/init.ts
1206
1320
  import { existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync9 } from "fs";
1207
- import { join as join8, dirname as dirname4, basename as basename2 } from "path";
1321
+ import { join as join8, dirname as dirname5, basename as basename2 } from "path";
1208
1322
  import { fileURLToPath as fileURLToPath2 } from "url";
1209
1323
  import * as childProcess2 from "child_process";
1210
1324
 
@@ -1392,7 +1506,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
1392
1506
 
1393
1507
  // src/commands/init-plays/registry.ts
1394
1508
  import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
1395
- import { dirname as dirname3, join as join6 } from "path";
1509
+ import { dirname as dirname4, join as join6 } from "path";
1396
1510
 
1397
1511
  // src/commands/init-plays/pattern-build.ts
1398
1512
  var patternBuildPlay = {
@@ -1448,7 +1562,7 @@ function readPlayState(statePath) {
1448
1562
  }
1449
1563
  }
1450
1564
  function writePlayState(statePath, state) {
1451
- mkdirSync(dirname3(statePath), { recursive: true });
1565
+ mkdirSync(dirname4(statePath), { recursive: true });
1452
1566
  writeFileSync2(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
1453
1567
  }
1454
1568
 
@@ -1758,7 +1872,7 @@ function scaffoldNextjs(cwd, json, filesCreated, filesSkipped, warnings) {
1758
1872
  config: data.config
1759
1873
  });
1760
1874
  const globalsPath = join8(cwd, "app", "globals.css");
1761
- const globalsDir = dirname4(globalsPath);
1875
+ const globalsDir = dirname5(globalsPath);
1762
1876
  mkdirSync2(globalsDir, { recursive: true });
1763
1877
  if (existsSync6(globalsPath)) {
1764
1878
  writeFileSync3(globalsPath, css, "utf-8");
@@ -1843,7 +1957,7 @@ function assertSpawnSuccess(result, label) {
1843
1957
  }
1844
1958
  function readVisorCliVersion() {
1845
1959
  try {
1846
- const here = dirname4(fileURLToPath2(import.meta.url));
1960
+ const here = dirname5(fileURLToPath2(import.meta.url));
1847
1961
  for (let i = 0; i < 5; i++) {
1848
1962
  const segments = new Array(i).fill("..");
1849
1963
  const candidate = join8(here, ...segments, "package.json");
@@ -1867,7 +1981,7 @@ import {
1867
1981
  existsSync as existsSync7,
1868
1982
  mkdirSync as mkdirSync3
1869
1983
  } from "fs";
1870
- import { dirname as dirname5, join as join9 } from "path";
1984
+ import { dirname as dirname6, join as join9 } from "path";
1871
1985
  function resolveOutputPath(registryPath, type, config, cwd) {
1872
1986
  let relativePath;
1873
1987
  if (type === "registry:block") {
@@ -1897,7 +2011,7 @@ function resolveOutputPath(registryPath, type, config, cwd) {
1897
2011
  return join9(cwd, registryPath);
1898
2012
  }
1899
2013
  function writeFile(filePath, content) {
1900
- const dir = dirname5(filePath);
2014
+ const dir = dirname6(filePath);
1901
2015
  if (!existsSync7(dir)) {
1902
2016
  mkdirSync3(dir, { recursive: true });
1903
2017
  }
@@ -2825,7 +2939,7 @@ function infoCommand(name, cwd, options = {}) {
2825
2939
 
2826
2940
  // src/commands/theme-apply.ts
2827
2941
  import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
2828
- import { resolve as resolve7, dirname as dirname7, join as join15 } from "path";
2942
+ import { resolve as resolve7, dirname as dirname8, join as join15 } from "path";
2829
2943
  import { generateTheme, generateThemeData as generateThemeData3 } from "@loworbitstudio/visor-theme-engine";
2830
2944
  import {
2831
2945
  nextjsAdapter as nextjsAdapter2,
@@ -2905,10 +3019,10 @@ function parseBlessedManifest(buildDir) {
2905
3019
 
2906
3020
  // src/lib/theme-apply-targets/globals-css.ts
2907
3021
  import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "fs";
2908
- import { dirname as dirname6, isAbsolute, join as join13, resolve as resolve5 } from "path";
3022
+ import { dirname as dirname7, isAbsolute, join as join13, resolve as resolve5 } from "path";
2909
3023
  function applyGlobalsCss(input) {
2910
3024
  const outFile = input.path !== void 0 ? resolveInsideBuild(input.buildDir, input.path) : resolveDefaultGlobalsCss(input.buildDir);
2911
- mkdirSync4(dirname6(outFile), { recursive: true });
3025
+ mkdirSync4(dirname7(outFile), { recursive: true });
2912
3026
  writeFileSync6(outFile, input.adapterCss, "utf-8");
2913
3027
  return outFile;
2914
3028
  }
@@ -3139,7 +3253,7 @@ function themeApplyCommand(file, cwd, options) {
3139
3253
  let totalBytes = 0;
3140
3254
  for (const [relPath, content] of Object.entries(fileMap.files)) {
3141
3255
  const filePath2 = join15(outputPath, relPath);
3142
- mkdirSync6(dirname7(filePath2), { recursive: true });
3256
+ mkdirSync6(dirname8(filePath2), { recursive: true });
3143
3257
  writeFileSync8(filePath2, content, "utf-8");
3144
3258
  totalBytes += content.length;
3145
3259
  }
@@ -3177,7 +3291,7 @@ function themeApplyCommand(file, cwd, options) {
3177
3291
  if (css === null) {
3178
3292
  process.exit(1);
3179
3293
  }
3180
- const outputDir = dirname7(outputPath);
3294
+ const outputDir = dirname8(outputPath);
3181
3295
  try {
3182
3296
  mkdirSync6(outputDir, { recursive: true });
3183
3297
  writeFileSync8(outputPath, css, "utf-8");
@@ -3973,7 +4087,7 @@ import { docsAdapter as docsAdapter2 } from "@loworbitstudio/visor-theme-engine/
3973
4087
 
3974
4088
  // src/utils/theme-helpers.ts
3975
4089
  import { existsSync as existsSync14, lstatSync, readdirSync as readdirSync5, readFileSync as readFileSync17, readlinkSync, realpathSync, statSync as statSync6 } from "fs";
3976
- import { resolve as resolve12, dirname as dirname8, join as join17 } from "path";
4090
+ import { resolve as resolve12, dirname as dirname9, join as join17 } from "path";
3977
4091
  import { execFileSync as execFileSync3 } from "child_process";
3978
4092
  function toSlug(name) {
3979
4093
  return name.toLowerCase().replace(/\s+/g, "-");
@@ -3987,7 +4101,7 @@ function findRepoRoot(startDir) {
3987
4101
  if (existsSync14(join17(current, "packages", "docs"))) {
3988
4102
  return current;
3989
4103
  }
3990
- const parent = dirname8(current);
4104
+ const parent = dirname9(current);
3991
4105
  if (parent === current) break;
3992
4106
  current = parent;
3993
4107
  }
@@ -4002,7 +4116,7 @@ function findMainRepoRoot(startDir) {
4002
4116
  }).trim();
4003
4117
  if (commonDir) {
4004
4118
  const absoluteCommonDir = resolve12(startDir, commonDir);
4005
- const candidate = dirname8(absoluteCommonDir);
4119
+ const candidate = dirname9(absoluteCommonDir);
4006
4120
  if (existsSync14(join17(candidate, "packages", "docs"))) {
4007
4121
  return candidate;
4008
4122
  }
@@ -4085,7 +4199,7 @@ function detectVisorWorkspace(cwd) {
4085
4199
  } catch {
4086
4200
  }
4087
4201
  }
4088
- const parent = dirname8(current);
4202
+ const parent = dirname9(current);
4089
4203
  if (parent === current) break;
4090
4204
  current = parent;
4091
4205
  }
@@ -5064,7 +5178,7 @@ import {
5064
5178
  readdirSync as readdirSync7,
5065
5179
  rmSync
5066
5180
  } from "fs";
5067
- import { join as join21, basename as basename5, dirname as dirname9 } from "path";
5181
+ import { join as join21, basename as basename5, dirname as dirname10 } from "path";
5068
5182
  import { generateThemeData as generateThemeData6 } from "@loworbitstudio/visor-theme-engine";
5069
5183
  import { flutterAdapter as flutterAdapter2 } from "@loworbitstudio/visor-theme-engine/adapters";
5070
5184
  function scanThemeDir2(dir) {
@@ -5318,7 +5432,7 @@ function themeBatchApplyFlutterCommand(cwd, options) {
5318
5432
  let totalFiles = 0;
5319
5433
  for (const [relPath, content] of Object.entries(packageFiles)) {
5320
5434
  const absPath = join21(outputDir, relPath);
5321
- mkdirSync9(dirname9(absPath), { recursive: true });
5435
+ mkdirSync9(dirname10(absPath), { recursive: true });
5322
5436
  writeFileSync13(absPath, content, "utf-8");
5323
5437
  totalFiles++;
5324
5438
  }
@@ -5326,7 +5440,7 @@ function themeBatchApplyFlutterCommand(cwd, options) {
5326
5440
  const themeBaseDir = join21(outputDir, "lib", "src", slug2);
5327
5441
  for (const [relPath, content] of Object.entries(tokenFiles)) {
5328
5442
  const absPath = join21(themeBaseDir, relPath);
5329
- mkdirSync9(dirname9(absPath), { recursive: true });
5443
+ mkdirSync9(dirname10(absPath), { recursive: true });
5330
5444
  writeFileSync13(absPath, content, "utf-8");
5331
5445
  totalFiles++;
5332
5446
  }
@@ -6402,13 +6516,13 @@ function migrateTokenSubstitutionCommand(targetArg, cwd, options) {
6402
6516
 
6403
6517
  // src/commands/sandbox/init.ts
6404
6518
  import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync28, rmSync as rmSync2 } from "fs";
6405
- import { isAbsolute as isAbsolute4, join as join27, resolve as resolve18, dirname as dirname11 } from "path";
6519
+ import { isAbsolute as isAbsolute4, join as join27, resolve as resolve18, dirname as dirname12 } from "path";
6406
6520
  import { fileURLToPath as fileURLToPath3 } from "url";
6407
6521
  import * as childProcess3 from "child_process";
6408
6522
 
6409
6523
  // src/commands/sandbox/parse-handoff.ts
6410
6524
  import { readFileSync as readFileSync26, existsSync as existsSync23 } from "fs";
6411
- import { dirname as dirname10, resolve as resolve17, isAbsolute as isAbsolute3 } from "path";
6525
+ import { dirname as dirname11, resolve as resolve17, isAbsolute as isAbsolute3 } from "path";
6412
6526
  function parseHandoff(handoffPath) {
6413
6527
  const text = readFileSync26(handoffPath, "utf-8");
6414
6528
  const lines2 = text.split("\n");
@@ -6449,7 +6563,7 @@ function extractRecipePath(text, handoffPath, warnings) {
6449
6563
  }
6450
6564
  const href = m[2].trim();
6451
6565
  if (isAbsolute3(href)) return href;
6452
- return resolve17(dirname10(handoffPath), href);
6566
+ return resolve17(dirname11(handoffPath), href);
6453
6567
  }
6454
6568
  var STATUS_PATTERNS = [
6455
6569
  { re: /NEW\s+gap/i, status: "gap-new" },
@@ -7620,7 +7734,7 @@ function loadKnownPrimitives() {
7620
7734
  }
7621
7735
  function readCliVersion() {
7622
7736
  try {
7623
- const here = dirname11(fileURLToPath3(import.meta.url));
7737
+ const here = dirname12(fileURLToPath3(import.meta.url));
7624
7738
  for (let i = 0; i < 6; i++) {
7625
7739
  const segments = new Array(i).fill("..");
7626
7740
  const candidate = join27(here, ...segments, "package.json");
@@ -8174,7 +8288,7 @@ function spawnCommand(cwd, options) {
8174
8288
 
8175
8289
  // src/commands/render.ts
8176
8290
  import { existsSync as existsSync30, mkdirSync as mkdirSync14, readFileSync as readFileSync32 } from "fs";
8177
- import { dirname as dirname12, isAbsolute as isAbsolute6, resolve as resolve20 } from "path";
8291
+ import { dirname as dirname13, isAbsolute as isAbsolute6, resolve as resolve20 } from "path";
8178
8292
  var FIXTURES = {
8179
8293
  "stat-card": {
8180
8294
  default: {
@@ -8536,7 +8650,7 @@ async function renderCommand(component, cwd, options) {
8536
8650
  const width = Number(options.width ?? 720);
8537
8651
  const height = Number(options.height ?? 640);
8538
8652
  const outPath = resolveOutPath(cwd, options.out, component, options.theme, mode, state);
8539
- mkdirSync14(dirname12(outPath), { recursive: true });
8653
+ mkdirSync14(dirname13(outPath), { recursive: true });
8540
8654
  const browser = await playwright.chromium.launch();
8541
8655
  let probe;
8542
8656
  try {
@@ -8648,7 +8762,7 @@ function formatBytes2(bytes) {
8648
8762
  }
8649
8763
 
8650
8764
  // src/index.ts
8651
- var __dirname2 = dirname13(fileURLToPath4(import.meta.url));
8765
+ var __dirname2 = dirname14(fileURLToPath4(import.meta.url));
8652
8766
  var pkg = JSON.parse(
8653
8767
  readFileSync33(join33(__dirname2, "..", "package.json"), "utf-8")
8654
8768
  );