@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 +47 -34
- package/dist/client/assets/index-JzB7eypf.css +1 -0
- package/{src/client/dist → dist/client}/index.html +2 -2
- package/package.json +3 -5
- package/src/client/app.tsx +0 -123
- package/src/client/components/shadow-controls.tsx +0 -287
- package/src/client/components/shadow-editor-panel.tsx +0 -163
- package/src/client/components/shadow-list.tsx +0 -343
- package/src/client/components/shadow-overview.tsx +0 -111
- package/src/client/components/shadow-preview-settings.tsx +0 -210
- package/src/client/components/shadow-preview.tsx +0 -34
- package/src/client/dist/assets/index-Bb9AiwJs.css +0 -1
- package/src/client/index.html +0 -13
- package/src/client/main.tsx +0 -5
- package/src/client/styles.css +0 -1
- /package/{src/client/dist/assets/index-CA2rtXvo.js → dist/client/assets/index-B-QvQdUy.js} +0 -0
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
|
|
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
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
|
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 (
|
|
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 =
|
|
473
|
-
const resolvedPath =
|
|
474
|
-
if (resolvedPath !== resolvedRoot && !resolvedPath.startsWith(resolvedRoot +
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
1365
|
+
var __dirname = path11.dirname(fileURLToPath(import.meta.url));
|
|
1355
1366
|
var require2 = createRequire(import.meta.url);
|
|
1356
|
-
var packageRoot = fs10.existsSync(
|
|
1367
|
+
var packageRoot = fs10.existsSync(path11.join(__dirname, "../package.json")) ? path11.resolve(__dirname, "..") : path11.resolve(__dirname, "../..");
|
|
1357
1368
|
function resolveInjectScript() {
|
|
1358
|
-
const compiledInject =
|
|
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 =
|
|
1363
|
-
const coreInject =
|
|
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 =
|
|
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 =
|
|
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-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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.
|
|
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
|
|
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",
|
package/src/client/app.tsx
DELETED
|
@@ -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
|
-
}
|