@designtools/shadows 0.1.11 → 0.1.12

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,23 @@ 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 builtIndex = config.clientDistRoot ? path4.join(config.clientDistRoot, "index.html") : null;
432
+ const hasBuiltClient = builtIndex && fs4.existsSync(builtIndex);
433
+ if (hasBuiltClient) {
434
+ app.use(express.static(config.clientDistRoot));
435
+ app.use((_req, res) => {
436
+ res.sendFile(builtIndex);
437
+ });
438
+ } else {
439
+ const vite = await createViteServer({
440
+ configFile: false,
441
+ root: config.clientRoot,
442
+ plugins: [react(), tailwindcss()],
443
+ server: { middlewareMode: true },
444
+ appType: "spa"
445
+ });
446
+ app.use(vite.middlewares);
447
+ }
438
448
  const server = app.listen(config.toolPort, () => {
439
449
  console.log(` Tool running at http://localhost:${config.toolPort}`);
440
450
  open(`http://localhost:${config.toolPort}`);
@@ -459,19 +469,19 @@ async function createToolServer(config) {
459
469
  }
460
470
 
461
471
  // ../core/src/server/safe-path.ts
462
- import path4 from "path";
472
+ import path5 from "path";
463
473
  function safePath(projectRoot, filePath) {
464
474
  if (!filePath || typeof filePath !== "string") {
465
475
  throw new Error("File path is required");
466
476
  }
467
- if (path4.isAbsolute(filePath)) {
477
+ if (path5.isAbsolute(filePath)) {
468
478
  throw new Error(
469
479
  `Absolute paths are not allowed: "${filePath}". Paths must be relative to the project root.`
470
480
  );
471
481
  }
472
- const resolvedRoot = path4.resolve(projectRoot);
473
- const resolvedPath = path4.resolve(resolvedRoot, filePath);
474
- if (resolvedPath !== resolvedRoot && !resolvedPath.startsWith(resolvedRoot + path4.sep)) {
482
+ const resolvedRoot = path5.resolve(projectRoot);
483
+ const resolvedPath = path5.resolve(resolvedRoot, filePath);
484
+ if (resolvedPath !== resolvedRoot && !resolvedPath.startsWith(resolvedRoot + path5.sep)) {
475
485
  throw new Error(
476
486
  `Path "${filePath}" resolves outside the project directory. Refusing to write.`
477
487
  );
@@ -482,11 +492,11 @@ function safePath(projectRoot, filePath) {
482
492
  // src/server/api/write-shadows.ts
483
493
  import { Router } from "express";
484
494
  import fs6 from "fs/promises";
485
- import path6 from "path";
495
+ import path7 from "path";
486
496
 
487
497
  // src/server/scanner/presets/w3c-design-tokens.ts
488
498
  import fs5 from "fs/promises";
489
- import path5 from "path";
499
+ import path6 from "path";
490
500
  async function findDesignTokenFiles(projectRoot) {
491
501
  const candidates = [
492
502
  "tokens",
@@ -499,11 +509,11 @@ async function findDesignTokenFiles(projectRoot) {
499
509
  const found = [];
500
510
  for (const dir of candidates) {
501
511
  try {
502
- const fullDir = path5.join(projectRoot, dir);
512
+ const fullDir = path6.join(projectRoot, dir);
503
513
  const entries = await fs5.readdir(fullDir);
504
514
  for (const entry of entries) {
505
515
  if (entry.endsWith(".tokens.json") || entry.endsWith(".tokens")) {
506
- found.push(path5.join(dir, entry));
516
+ found.push(path6.join(dir, entry));
507
517
  }
508
518
  }
509
519
  } catch {
@@ -516,7 +526,7 @@ async function scanDesignTokenShadows(projectRoot, tokenFiles) {
516
526
  const tokens = [];
517
527
  for (const file of files) {
518
528
  try {
519
- const content = await fs5.readFile(path5.join(projectRoot, file), "utf-8");
529
+ const content = await fs5.readFile(path6.join(projectRoot, file), "utf-8");
520
530
  const parsed = JSON.parse(content);
521
531
  extractShadowTokens(parsed, [], file, tokens);
522
532
  } catch {
@@ -729,7 +739,7 @@ function createShadowsRouter(projectRoot) {
729
739
  const { filePath, shadows } = req.body;
730
740
  const fullPath = safePath(projectRoot, filePath);
731
741
  const tokens = buildDesignTokensJson(shadows);
732
- await fs6.mkdir(path6.dirname(fullPath), { recursive: true });
742
+ await fs6.mkdir(path7.dirname(fullPath), { recursive: true });
733
743
  await writeDesignTokensFile(fullPath, tokens);
734
744
  res.json({ ok: true, filePath, tokenCount: shadows.length });
735
745
  } catch (err) {
@@ -850,13 +860,13 @@ import { Router as Router2 } from "express";
850
860
 
851
861
  // ../core/src/scanner/scan-tokens.ts
852
862
  import fs7 from "fs/promises";
853
- import path7 from "path";
863
+ import path8 from "path";
854
864
  async function scanTokens(projectRoot, framework) {
855
865
  if (framework.cssFiles.length === 0) {
856
866
  return { tokens: [], cssFilePath: "", groups: {} };
857
867
  }
858
868
  const cssFilePath = framework.cssFiles[0];
859
- const fullPath = path7.join(projectRoot, cssFilePath);
869
+ const fullPath = path8.join(projectRoot, cssFilePath);
860
870
  const css = await fs7.readFile(fullPath, "utf-8");
861
871
  const rootTokens = parseBlock(css, ":root");
862
872
  const darkTokens = parseBlock(css, ".dark");
@@ -966,7 +976,7 @@ function detectColorFormat(value) {
966
976
 
967
977
  // src/server/scanner/scan-shadows.ts
968
978
  import fs9 from "fs/promises";
969
- import path9 from "path";
979
+ import path10 from "path";
970
980
 
971
981
  // src/server/scanner/presets/tailwind.ts
972
982
  var TAILWIND_SHADOW_PRESETS = [
@@ -1014,7 +1024,7 @@ var TAILWIND_SHADOW_PRESETS = [
1014
1024
 
1015
1025
  // src/server/scanner/presets/bootstrap.ts
1016
1026
  import fs8 from "fs/promises";
1017
- import path8 from "path";
1027
+ import path9 from "path";
1018
1028
  var BOOTSTRAP_SHADOW_PRESETS = [
1019
1029
  {
1020
1030
  name: "box-shadow-sm",
@@ -1037,7 +1047,7 @@ async function scanBootstrapScssOverrides(projectRoot, scssFiles) {
1037
1047
  const overrides = [];
1038
1048
  for (const file of scssFiles) {
1039
1049
  try {
1040
- const content = await fs8.readFile(path8.join(projectRoot, file), "utf-8");
1050
+ const content = await fs8.readFile(path9.join(projectRoot, file), "utf-8");
1041
1051
  const lines = content.split("\n");
1042
1052
  for (const line of lines) {
1043
1053
  const match = line.match(
@@ -1065,7 +1075,7 @@ async function scanBootstrapCssOverrides(projectRoot, cssFiles) {
1065
1075
  const overrides = [];
1066
1076
  for (const file of cssFiles) {
1067
1077
  try {
1068
- const content = await fs8.readFile(path8.join(projectRoot, file), "utf-8");
1078
+ const content = await fs8.readFile(path9.join(projectRoot, file), "utf-8");
1069
1079
  const propRegex = /(--bs-box-shadow(?:-sm|-lg|-inset)?)\s*:\s*([^;]+);/g;
1070
1080
  let match;
1071
1081
  while ((match = propRegex.exec(content)) !== null) {
@@ -1213,7 +1223,7 @@ async function scanCustomShadows(projectRoot, cssFiles) {
1213
1223
  const shadows = [];
1214
1224
  for (const file of cssFiles) {
1215
1225
  try {
1216
- const css = await fs9.readFile(path9.join(projectRoot, file), "utf-8");
1226
+ const css = await fs9.readFile(path10.join(projectRoot, file), "utf-8");
1217
1227
  const rootTokens = parseBlock(css, ":root");
1218
1228
  for (const [name, value] of rootTokens) {
1219
1229
  if (name.includes("shadow") || isShadowValue(value)) {
@@ -1351,32 +1361,34 @@ function createShadowsScanRouter(projectRoot) {
1351
1361
  }
1352
1362
 
1353
1363
  // src/server/index.ts
1354
- var __dirname = path10.dirname(fileURLToPath(import.meta.url));
1364
+ var __dirname = path11.dirname(fileURLToPath(import.meta.url));
1355
1365
  var require2 = createRequire(import.meta.url);
1356
- var packageRoot = fs10.existsSync(path10.join(__dirname, "../package.json")) ? path10.resolve(__dirname, "..") : path10.resolve(__dirname, "../..");
1366
+ var packageRoot = fs10.existsSync(path11.join(__dirname, "../package.json")) ? path11.resolve(__dirname, "..") : path11.resolve(__dirname, "../..");
1357
1367
  function resolveInjectScript() {
1358
- const compiledInject = path10.join(packageRoot, "dist/inject/selection.js");
1368
+ const compiledInject = path11.join(packageRoot, "dist/inject/selection.js");
1359
1369
  if (fs10.existsSync(compiledInject)) return compiledInject;
1360
1370
  try {
1361
1371
  const corePkg = require2.resolve("@designtools/core/package.json");
1362
- const coreRoot = path10.dirname(corePkg);
1363
- const coreInject = path10.join(coreRoot, "src/inject/selection.ts");
1372
+ const coreRoot = path11.dirname(corePkg);
1373
+ const coreInject = path11.join(coreRoot, "src/inject/selection.ts");
1364
1374
  if (fs10.existsSync(coreInject)) return coreInject;
1365
1375
  } catch {
1366
1376
  }
1367
- const monorepoInject = path10.join(packageRoot, "../core/src/inject/selection.ts");
1377
+ const monorepoInject = path11.join(packageRoot, "../core/src/inject/selection.ts");
1368
1378
  if (fs10.existsSync(monorepoInject)) return monorepoInject;
1369
1379
  throw new Error(
1370
1380
  "Could not find inject script (selection.ts). Ensure @designtools/core is installed."
1371
1381
  );
1372
1382
  }
1373
1383
  async function startShadowsServer(preflight) {
1374
- const clientRoot = path10.join(packageRoot, "src/client");
1384
+ const clientRoot = path11.join(packageRoot, "src/client");
1385
+ const clientDistRoot = path11.join(packageRoot, "dist/client");
1375
1386
  const actualInjectPath = resolveInjectScript();
1376
1387
  const { app, wss, projectRoot } = await createToolServer({
1377
1388
  targetPort: preflight.targetPort,
1378
1389
  toolPort: preflight.toolPort,
1379
1390
  clientRoot,
1391
+ clientDistRoot,
1380
1392
  injectScriptPath: actualInjectPath,
1381
1393
  setupRoutes: (app2, projectRoot2) => {
1382
1394
  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.12",
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
- }