@designtools/shadows 0.1.11 → 0.1.13

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/dist/cli.js CHANGED
@@ -319,7 +319,7 @@ async function bootstrap(config) {
319
319
  }
320
320
 
321
321
  // src/server/index.ts
322
- import path10 from "path";
322
+ import path11 from "path";
323
323
  import fs10 from "fs";
324
324
  import { fileURLToPath } from "url";
325
325
  import { createRequire } from "module";
@@ -330,6 +330,7 @@ import { createProxyMiddleware } from "http-proxy-middleware";
330
330
  import httpProxy from "http-proxy";
331
331
  import { WebSocketServer } from "ws";
332
332
  import fs4 from "fs";
333
+ import path4 from "path";
333
334
  import zlib from "zlib";
334
335
  import open from "open";
335
336
  import { createServer as createViteServer } from "vite";
@@ -427,14 +428,24 @@ async function createToolServer(config) {
427
428
  if (config.setupRoutes) {
428
429
  config.setupRoutes(app, projectRoot);
429
430
  }
430
- const vite = await createViteServer({
431
- configFile: false,
432
- root: config.clientRoot,
433
- plugins: [react(), tailwindcss()],
434
- server: { middlewareMode: true },
435
- appType: "spa"
436
- });
437
- app.use(vite.middlewares);
431
+ const distRoot = config.clientDistRoot ? path4.resolve(config.clientDistRoot) : null;
432
+ const builtIndex = distRoot ? path4.join(distRoot, "index.html") : null;
433
+ const hasBuiltClient = builtIndex && fs4.existsSync(builtIndex);
434
+ if (hasBuiltClient) {
435
+ app.use(express.static(distRoot));
436
+ app.use((_req, res) => {
437
+ res.sendFile(builtIndex);
438
+ });
439
+ } else {
440
+ const vite = await createViteServer({
441
+ configFile: false,
442
+ root: config.clientRoot,
443
+ plugins: [react(), tailwindcss()],
444
+ server: { middlewareMode: true },
445
+ appType: "spa"
446
+ });
447
+ app.use(vite.middlewares);
448
+ }
438
449
  const server = app.listen(config.toolPort, () => {
439
450
  console.log(` Tool running at http://localhost:${config.toolPort}`);
440
451
  open(`http://localhost:${config.toolPort}`);
@@ -459,19 +470,19 @@ async function createToolServer(config) {
459
470
  }
460
471
 
461
472
  // ../core/src/server/safe-path.ts
462
- import path4 from "path";
473
+ import path5 from "path";
463
474
  function safePath(projectRoot, filePath) {
464
475
  if (!filePath || typeof filePath !== "string") {
465
476
  throw new Error("File path is required");
466
477
  }
467
- if (path4.isAbsolute(filePath)) {
478
+ if (path5.isAbsolute(filePath)) {
468
479
  throw new Error(
469
480
  `Absolute paths are not allowed: "${filePath}". Paths must be relative to the project root.`
470
481
  );
471
482
  }
472
- const resolvedRoot = path4.resolve(projectRoot);
473
- const resolvedPath = path4.resolve(resolvedRoot, filePath);
474
- if (resolvedPath !== resolvedRoot && !resolvedPath.startsWith(resolvedRoot + path4.sep)) {
483
+ const resolvedRoot = path5.resolve(projectRoot);
484
+ const resolvedPath = path5.resolve(resolvedRoot, filePath);
485
+ if (resolvedPath !== resolvedRoot && !resolvedPath.startsWith(resolvedRoot + path5.sep)) {
475
486
  throw new Error(
476
487
  `Path "${filePath}" resolves outside the project directory. Refusing to write.`
477
488
  );
@@ -482,11 +493,11 @@ function safePath(projectRoot, filePath) {
482
493
  // src/server/api/write-shadows.ts
483
494
  import { Router } from "express";
484
495
  import fs6 from "fs/promises";
485
- import path6 from "path";
496
+ import path7 from "path";
486
497
 
487
498
  // src/server/scanner/presets/w3c-design-tokens.ts
488
499
  import fs5 from "fs/promises";
489
- import path5 from "path";
500
+ import path6 from "path";
490
501
  async function findDesignTokenFiles(projectRoot) {
491
502
  const candidates = [
492
503
  "tokens",
@@ -499,11 +510,11 @@ async function findDesignTokenFiles(projectRoot) {
499
510
  const found = [];
500
511
  for (const dir of candidates) {
501
512
  try {
502
- const fullDir = path5.join(projectRoot, dir);
513
+ const fullDir = path6.join(projectRoot, dir);
503
514
  const entries = await fs5.readdir(fullDir);
504
515
  for (const entry of entries) {
505
516
  if (entry.endsWith(".tokens.json") || entry.endsWith(".tokens")) {
506
- found.push(path5.join(dir, entry));
517
+ found.push(path6.join(dir, entry));
507
518
  }
508
519
  }
509
520
  } catch {
@@ -516,7 +527,7 @@ async function scanDesignTokenShadows(projectRoot, tokenFiles) {
516
527
  const tokens = [];
517
528
  for (const file of files) {
518
529
  try {
519
- const content = await fs5.readFile(path5.join(projectRoot, file), "utf-8");
530
+ const content = await fs5.readFile(path6.join(projectRoot, file), "utf-8");
520
531
  const parsed = JSON.parse(content);
521
532
  extractShadowTokens(parsed, [], file, tokens);
522
533
  } catch {
@@ -729,7 +740,7 @@ function createShadowsRouter(projectRoot) {
729
740
  const { filePath, shadows } = req.body;
730
741
  const fullPath = safePath(projectRoot, filePath);
731
742
  const tokens = buildDesignTokensJson(shadows);
732
- await fs6.mkdir(path6.dirname(fullPath), { recursive: true });
743
+ await fs6.mkdir(path7.dirname(fullPath), { recursive: true });
733
744
  await writeDesignTokensFile(fullPath, tokens);
734
745
  res.json({ ok: true, filePath, tokenCount: shadows.length });
735
746
  } catch (err) {
@@ -850,13 +861,13 @@ import { Router as Router2 } from "express";
850
861
 
851
862
  // ../core/src/scanner/scan-tokens.ts
852
863
  import fs7 from "fs/promises";
853
- import path7 from "path";
864
+ import path8 from "path";
854
865
  async function scanTokens(projectRoot, framework) {
855
866
  if (framework.cssFiles.length === 0) {
856
867
  return { tokens: [], cssFilePath: "", groups: {} };
857
868
  }
858
869
  const cssFilePath = framework.cssFiles[0];
859
- const fullPath = path7.join(projectRoot, cssFilePath);
870
+ const fullPath = path8.join(projectRoot, cssFilePath);
860
871
  const css = await fs7.readFile(fullPath, "utf-8");
861
872
  const rootTokens = parseBlock(css, ":root");
862
873
  const darkTokens = parseBlock(css, ".dark");
@@ -966,7 +977,7 @@ function detectColorFormat(value) {
966
977
 
967
978
  // src/server/scanner/scan-shadows.ts
968
979
  import fs9 from "fs/promises";
969
- import path9 from "path";
980
+ import path10 from "path";
970
981
 
971
982
  // src/server/scanner/presets/tailwind.ts
972
983
  var TAILWIND_SHADOW_PRESETS = [
@@ -1014,7 +1025,7 @@ var TAILWIND_SHADOW_PRESETS = [
1014
1025
 
1015
1026
  // src/server/scanner/presets/bootstrap.ts
1016
1027
  import fs8 from "fs/promises";
1017
- import path8 from "path";
1028
+ import path9 from "path";
1018
1029
  var BOOTSTRAP_SHADOW_PRESETS = [
1019
1030
  {
1020
1031
  name: "box-shadow-sm",
@@ -1037,7 +1048,7 @@ async function scanBootstrapScssOverrides(projectRoot, scssFiles) {
1037
1048
  const overrides = [];
1038
1049
  for (const file of scssFiles) {
1039
1050
  try {
1040
- const content = await fs8.readFile(path8.join(projectRoot, file), "utf-8");
1051
+ const content = await fs8.readFile(path9.join(projectRoot, file), "utf-8");
1041
1052
  const lines = content.split("\n");
1042
1053
  for (const line of lines) {
1043
1054
  const match = line.match(
@@ -1065,7 +1076,7 @@ async function scanBootstrapCssOverrides(projectRoot, cssFiles) {
1065
1076
  const overrides = [];
1066
1077
  for (const file of cssFiles) {
1067
1078
  try {
1068
- const content = await fs8.readFile(path8.join(projectRoot, file), "utf-8");
1079
+ const content = await fs8.readFile(path9.join(projectRoot, file), "utf-8");
1069
1080
  const propRegex = /(--bs-box-shadow(?:-sm|-lg|-inset)?)\s*:\s*([^;]+);/g;
1070
1081
  let match;
1071
1082
  while ((match = propRegex.exec(content)) !== null) {
@@ -1213,7 +1224,7 @@ async function scanCustomShadows(projectRoot, cssFiles) {
1213
1224
  const shadows = [];
1214
1225
  for (const file of cssFiles) {
1215
1226
  try {
1216
- const css = await fs9.readFile(path9.join(projectRoot, file), "utf-8");
1227
+ const css = await fs9.readFile(path10.join(projectRoot, file), "utf-8");
1217
1228
  const rootTokens = parseBlock(css, ":root");
1218
1229
  for (const [name, value] of rootTokens) {
1219
1230
  if (name.includes("shadow") || isShadowValue(value)) {
@@ -1351,32 +1362,34 @@ function createShadowsScanRouter(projectRoot) {
1351
1362
  }
1352
1363
 
1353
1364
  // src/server/index.ts
1354
- var __dirname = path10.dirname(fileURLToPath(import.meta.url));
1365
+ var __dirname = path11.dirname(fileURLToPath(import.meta.url));
1355
1366
  var require2 = createRequire(import.meta.url);
1356
- var packageRoot = fs10.existsSync(path10.join(__dirname, "../package.json")) ? path10.resolve(__dirname, "..") : path10.resolve(__dirname, "../..");
1367
+ var packageRoot = fs10.existsSync(path11.join(__dirname, "../package.json")) ? path11.resolve(__dirname, "..") : path11.resolve(__dirname, "../..");
1357
1368
  function resolveInjectScript() {
1358
- const compiledInject = path10.join(packageRoot, "dist/inject/selection.js");
1369
+ const compiledInject = path11.join(packageRoot, "dist/inject/selection.js");
1359
1370
  if (fs10.existsSync(compiledInject)) return compiledInject;
1360
1371
  try {
1361
1372
  const corePkg = require2.resolve("@designtools/core/package.json");
1362
- const coreRoot = path10.dirname(corePkg);
1363
- const coreInject = path10.join(coreRoot, "src/inject/selection.ts");
1373
+ const coreRoot = path11.dirname(corePkg);
1374
+ const coreInject = path11.join(coreRoot, "src/inject/selection.ts");
1364
1375
  if (fs10.existsSync(coreInject)) return coreInject;
1365
1376
  } catch {
1366
1377
  }
1367
- const monorepoInject = path10.join(packageRoot, "../core/src/inject/selection.ts");
1378
+ const monorepoInject = path11.join(packageRoot, "../core/src/inject/selection.ts");
1368
1379
  if (fs10.existsSync(monorepoInject)) return monorepoInject;
1369
1380
  throw new Error(
1370
1381
  "Could not find inject script (selection.ts). Ensure @designtools/core is installed."
1371
1382
  );
1372
1383
  }
1373
1384
  async function startShadowsServer(preflight) {
1374
- const clientRoot = path10.join(packageRoot, "src/client");
1385
+ const clientRoot = path11.join(packageRoot, "src/client");
1386
+ const clientDistRoot = path11.join(packageRoot, "dist/client");
1375
1387
  const actualInjectPath = resolveInjectScript();
1376
1388
  const { app, wss, projectRoot } = await createToolServer({
1377
1389
  targetPort: preflight.targetPort,
1378
1390
  toolPort: preflight.toolPort,
1379
1391
  clientRoot,
1392
+ clientDistRoot,
1380
1393
  injectScriptPath: actualInjectPath,
1381
1394
  setupRoutes: (app2, projectRoot2) => {
1382
1395
  app2.use("/api/shadows", createShadowsRouter(projectRoot2));
@@ -0,0 +1 @@
1
+ /*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--spacing:.25rem;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--shadow-sm:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{box-sizing:border-box;margin:0;padding:0}}@layer components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.right-0{right:calc(var(--spacing)*0)}.mx-4{margin-inline:calc(var(--spacing)*4)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2\.5{margin-top:calc(var(--spacing)*2.5)}.mb-0{margin-bottom:calc(var(--spacing)*0)}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-8{height:calc(var(--spacing)*8)}.h-11{height:calc(var(--spacing)*11)}.h-20{height:calc(var(--spacing)*20)}.h-full{height:100%}.h-screen{height:100vh}.w-1{width:calc(var(--spacing)*1)}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-5{width:calc(var(--spacing)*5)}.w-14{width:calc(var(--spacing)*14)}.w-20{width:calc(var(--spacing)*20)}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-0{flex:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.cursor-ew-resize{cursor:ew-resize}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-3\.5{gap:calc(var(--spacing)*3.5)}.gap-x-2{column-gap:calc(var(--spacing)*2)}.gap-y-1\.5{row-gap:calc(var(--spacing)*1.5)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.p-2\.5{padding:calc(var(--spacing)*2.5)}.p-3{padding:calc(var(--spacing)*3)}.p-6{padding:calc(var(--spacing)*6)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-6{padding-block:calc(var(--spacing)*6)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.uppercase{text-transform:uppercase}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}}:root{--studio-bg:#0a0a0c;--studio-surface:#1a1a1e;--studio-surface-hover:#242428;--studio-surface-active:#2e2e34;--studio-border:#3c3c44;--studio-border-subtle:#2e2e36;--studio-text:#f4f4f8;--studio-text-muted:#b0b0c0;--studio-text-dimmed:#808094;--studio-input-bg:#121216;--studio-accent:#60a5fa;--studio-accent-hover:#3b82f6;--studio-accent-muted:#60a5fa29;--studio-success:#22c55e;--studio-warning:#f59e0b;--studio-danger:#ef4444}body{background:var(--studio-bg);color:var(--studio-text);-webkit-font-smoothing:antialiased;height:100vh;font-family:-apple-system,BlinkMacSystemFont,Inter,Segoe UI,system-ui,sans-serif;font-size:12px;overflow:hidden}#root{height:100vh}.studio-scrollbar::-webkit-scrollbar{width:5px}.studio-scrollbar::-webkit-scrollbar-track{background:0 0}.studio-scrollbar::-webkit-scrollbar-thumb{background:var(--studio-border);border-radius:3px}.studio-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--studio-text-muted)}.studio-icon-btn{width:28px;height:28px;color:var(--studio-text-muted);cursor:pointer;background:0 0;border:none;border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;transition:background .1s,color .1s;display:inline-flex}.studio-icon-btn:hover{background:var(--studio-surface-hover);color:var(--studio-text)}.studio-icon-btn.active{background:var(--studio-accent);color:#fff}.studio-segmented{background:var(--studio-input-bg);border-radius:6px;gap:1px;padding:2px;display:inline-flex}.studio-segmented button{color:var(--studio-text-dimmed);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;gap:4px;padding:4px 10px;font-size:11px;font-weight:500;transition:background .1s,color .1s;display:inline-flex}.studio-segmented button:hover{color:var(--studio-text);background:#ffffff0a}.studio-segmented button.active{background:var(--studio-surface-hover);color:var(--studio-text);box-shadow:0 1px 2px #0003}.studio-section-hdr{text-transform:uppercase;letter-spacing:.05em;color:var(--studio-text-muted);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;align-items:center;gap:5px;width:100%;padding:12px 16px;font-size:9px;font-weight:600;display:flex}.studio-section-hdr:hover{color:var(--studio-text)}.studio-section-hdr .count{color:var(--studio-text-dimmed);margin-left:auto;font-size:9px;font-weight:400}.studio-section-hdr svg{flex-shrink:0;width:10px;height:10px;transition:transform .15s}.studio-prop-row{align-items:center;gap:8px;min-height:26px;padding:2px 16px;display:flex}.studio-prop-label{color:var(--studio-text-dimmed);text-transform:uppercase;letter-spacing:.03em;flex-shrink:0;min-width:42px;font-size:10px;font-weight:500}.studio-input{background:var(--studio-input-bg);border:1px solid var(--studio-border-subtle);color:var(--studio-text);border-radius:4px;outline:none;min-width:0;padding:4px 6px;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:11px;transition:border-color .15s}.studio-input:focus{border-color:var(--studio-accent)}.studio-input::placeholder{color:var(--studio-text-dimmed)}.studio-select{background:var(--studio-input-bg);border:1px solid var(--studio-border-subtle);color:var(--studio-text);cursor:pointer;border-radius:4px;outline:none;min-width:0;padding:3px 6px;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:11px;transition:border-color .15s}.studio-select:focus{border-color:var(--studio-accent)}.studio-two-col{grid-template-columns:1fr 1fr;gap:4px 8px;display:grid}.studio-swatch{border:1px solid var(--studio-border);cursor:pointer;background-image:linear-gradient(var(--swatch-color,transparent),var(--swatch-color,transparent)),linear-gradient(45deg,#1a1a24 25%,transparent 25%),linear-gradient(-45deg,#1a1a24 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#1a1a24 75%),linear-gradient(-45deg,transparent 75%,#1a1a24 75%);background-position:0 0,0 0,0 3px,3px -3px,-3px 0;background-size:cover,6px 6px,6px 6px,6px 6px,6px 6px;border-radius:4px;flex-shrink:0;width:18px;height:18px;transition:box-shadow .1s}.studio-swatch:hover{box-shadow:0 0 0 1px var(--studio-accent)}.studio-bp-btn{border:1px solid var(--studio-border-subtle);background:var(--studio-input-bg);color:var(--studio-text-muted);cursor:pointer;border-radius:4px;justify-content:center;align-items:center;padding:3px 8px;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:11px;font-weight:500;transition:background .1s,color .1s,border-color .1s;display:inline-flex}.studio-bp-btn:hover{background:var(--studio-surface-hover);border-color:var(--studio-border);color:var(--studio-text)}.studio-bp-btn.active{background:var(--studio-accent-muted);border-color:var(--studio-accent);color:var(--studio-accent)}input[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--studio-border);border-radius:2px;outline:none;height:3px}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--studio-accent);cursor:pointer;border:2px solid var(--studio-bg);border-radius:50%;width:12px;height:12px}input[type=range]::-webkit-slider-thumb:hover{background:var(--studio-accent-hover)}.studio-color-trigger{background:var(--studio-input-bg);border:1px solid var(--studio-border-subtle);cursor:pointer;width:100%;color:var(--studio-text);text-align:left;border-radius:4px;align-items:center;gap:8px;padding:4px 6px;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:11px;transition:border-color .15s;display:flex}.studio-color-trigger:hover{border-color:var(--studio-border)}.studio-color-trigger:focus{border-color:var(--studio-accent);outline:none}.studio-color-trigger svg{width:10px;height:10px;color:var(--studio-text-dimmed);flex-shrink:0;margin-left:auto}.studio-popover{z-index:10000;background:var(--studio-surface);border:1px solid var(--studio-border);border-radius:8px;flex-direction:column;width:240px;max-height:400px;padding:6px;display:flex;position:fixed;box-shadow:0 8px 24px #00000080,0 2px 8px #0000004d}.studio-popover-swatch{border:1px solid var(--studio-border);background-image:linear-gradient(var(--swatch-color,transparent),var(--swatch-color,transparent)),linear-gradient(45deg,#1a1a24 25%,transparent 25%),linear-gradient(-45deg,#1a1a24 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#1a1a24 75%),linear-gradient(-45deg,transparent 75%,#1a1a24 75%);background-position:0 0,0 0,0 4px,4px -4px,-4px 0;background-size:cover,8px 8px,8px 8px,8px 8px,8px 8px;border-radius:6px;flex-shrink:0;width:100%;height:48px;margin-bottom:6px}.studio-popover-list{flex-direction:column;gap:1px;max-height:280px;display:flex;overflow-y:auto}.studio-popover-list::-webkit-scrollbar{width:4px}.studio-popover-list::-webkit-scrollbar-thumb{background:var(--studio-border);border-radius:2px}.studio-popover-item{cursor:pointer;text-align:left;color:var(--studio-text);background:0 0;border:none;border-radius:4px;align-items:center;gap:8px;width:100%;padding:5px 8px;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:11px;transition:background .1s;display:flex}.studio-popover-item:hover{background:var(--studio-surface-hover)}.studio-popover-item.active{background:var(--studio-accent-muted);color:var(--studio-accent)}.studio-popover-item .studio-swatch{border-radius:3px;width:16px;height:16px}.studio-tab-explainer{color:var(--studio-text-muted);background:var(--studio-input-bg);align-items:flex-start;gap:8px;padding:12px 14px 13px;font-size:13px;line-height:1.45;display:flex}.studio-tab-explainer svg{width:14px;height:14px;color:var(--studio-text-dimmed);flex-shrink:0;margin-top:2px}.studio-tab-explainer .studio-explainer-file{color:var(--studio-text-dimmed);margin-top:2px;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:10px}.studio-tree{border-left:1px solid var(--studio-border-subtle);margin-bottom:2px;margin-left:16px;position:relative}.studio-tree-node{position:relative}.studio-tree-node:before{content:"";background:var(--studio-border-subtle);width:10px;height:1px;position:absolute;top:14px;left:0}.studio-tree-node:last-child:after{content:"";background:var(--studio-surface);width:1px;position:absolute;top:14px;bottom:0;left:-1px}.studio-tree-node>button{padding-left:14px}.studio-tree-node .studio-section-hdr{padding:5px 16px 5px 14px}.studio-tree-content{padding-bottom:6px;padding-left:14px;padding-right:8px}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Design Tools — Shadows</title>
7
- <script type="module" crossorigin src="/assets/index-CA2rtXvo.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-Bb9AiwJs.css">
7
+ <script type="module" crossorigin src="/assets/index-B-QvQdUy.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-JzB7eypf.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@designtools/shadows",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Visual shadow editing CLI — scan, preview, and edit box-shadow values in your project",
5
5
  "type": "module",
6
6
  "license": "CC-BY-NC-4.0",
@@ -11,16 +11,14 @@
11
11
  "scripts": {
12
12
  "dev": "tsx src/cli.ts",
13
13
  "dev:client": "vite dev src/client",
14
- "build": "vite build src/client && tsup && node scripts/build-inject.mjs",
14
+ "build": "vite build && tsup && node scripts/build-inject.mjs",
15
15
  "prepublishOnly": "npm run build"
16
16
  },
17
17
  "bin": {
18
18
  "designtools-shadows": "dist/cli.js"
19
19
  },
20
20
  "files": [
21
- "dist",
22
- "src/client",
23
- "src/inject"
21
+ "dist"
24
22
  ],
25
23
  "dependencies": {
26
24
  "@radix-ui/react-icons": "^1.3.2",
@@ -1,123 +0,0 @@
1
- import { useState, useEffect, useRef, useCallback } from "react";
2
- import { ShadowIcon } from "@radix-ui/react-icons";
3
- import { ToolChrome } from "@designtools/core/client/components/tool-chrome";
4
- import {
5
- sendToIframe,
6
- onIframeMessage,
7
- type ElementData,
8
- } from "@designtools/core/client/lib/iframe-bridge";
9
- import { ShadowEditorPanel } from "./components/shadow-editor-panel.js";
10
-
11
- export interface ShadowsScanData {
12
- framework: { name: string; cssFiles: string[] };
13
- styling: { type: string; cssFiles: string[] };
14
- shadows: {
15
- shadows: any[];
16
- cssFilePath: string;
17
- stylingType: string;
18
- };
19
- }
20
-
21
- export function App() {
22
- const [scanData, setScanData] = useState<ShadowsScanData | null>(null);
23
- const [selectedElement, setSelectedElement] = useState<ElementData | null>(null);
24
- const [selectionMode, setSelectionMode] = useState(false);
25
- const [theme, setTheme] = useState<"light" | "dark">("light");
26
- const [viewportWidth, setViewportWidth] = useState<number | "fill">("fill");
27
- const [iframePath, setIframePath] = useState("/");
28
- const iframeRef = useRef<HTMLIFrameElement>(null);
29
-
30
- useEffect(() => {
31
- fetch("/scan/all")
32
- .then((r) => r.json())
33
- .then(setScanData)
34
- .catch(console.error);
35
- }, []);
36
-
37
- useEffect(() => {
38
- return onIframeMessage((msg) => {
39
- if (msg.type === "tool:elementSelected") {
40
- setSelectedElement(msg.data);
41
- }
42
- if (msg.type === "tool:pathChanged") {
43
- setIframePath(msg.path);
44
- }
45
- if (msg.type === "tool:injectedReady" && iframeRef.current) {
46
- if (selectionMode) {
47
- sendToIframe(iframeRef.current, {
48
- type: "tool:enterSelectionMode",
49
- });
50
- }
51
- }
52
- });
53
- }, [selectionMode]);
54
-
55
- useEffect(() => {
56
- if (!iframeRef.current) return;
57
- sendToIframe(iframeRef.current, {
58
- type: selectionMode
59
- ? "tool:enterSelectionMode"
60
- : "tool:exitSelectionMode",
61
- });
62
- }, [selectionMode]);
63
-
64
- const toggleTheme = useCallback(() => {
65
- const newTheme = theme === "light" ? "dark" : "light";
66
- setTheme(newTheme);
67
- if (iframeRef.current) {
68
- sendToIframe(iframeRef.current, {
69
- type: "tool:setTheme",
70
- theme: newTheme,
71
- });
72
- }
73
- }, [theme]);
74
-
75
- const previewShadow = useCallback(
76
- (variableName: string, value: string, shadowName?: string) => {
77
- if (!iframeRef.current) return;
78
-
79
- // Tailwind v4 compiles shadow utilities (.shadow-sm) to use --tw-shadow
80
- // internally, so setting --shadow-sm on :root has no effect.
81
- // Instead, inject a <style> that overrides the utility class.
82
- const isTailwind = scanData?.styling.type === "tailwind-v4";
83
- if (isTailwind && shadowName) {
84
- sendToIframe(iframeRef.current, {
85
- type: "tool:previewShadow",
86
- className: shadowName,
87
- value,
88
- });
89
- } else {
90
- sendToIframe(iframeRef.current, {
91
- type: "tool:setProperty",
92
- token: variableName,
93
- value,
94
- });
95
- }
96
- },
97
- [scanData]
98
- );
99
-
100
- return (
101
- <ToolChrome
102
- toolName="Shadows"
103
- toolIcon={<ShadowIcon style={{ width: 15, height: 15 }} />}
104
- showSelectionMode={false}
105
- selectionMode={selectionMode}
106
- onToggleSelectionMode={() => setSelectionMode((s) => !s)}
107
- theme={theme}
108
- onToggleTheme={toggleTheme}
109
- viewportWidth={viewportWidth}
110
- onViewportWidthChange={setViewportWidth}
111
- iframePath={iframePath}
112
- onIframePathChange={setIframePath}
113
- iframeRef={iframeRef}
114
- editorPanel={
115
- <ShadowEditorPanel
116
- scanData={scanData}
117
- selectedElement={selectedElement}
118
- onPreviewShadow={previewShadow}
119
- />
120
- }
121
- />
122
- );
123
- }