@designtools/shadows 0.1.10 → 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 +46 -34
- package/dist/client/assets/index-JzB7eypf.css +1 -0
- package/{src/client/dist → dist/client}/index.html +2 -2
- package/dist/inject/selection.js +332 -0
- 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,23 @@ 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 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
|
|
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 (
|
|
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 =
|
|
473
|
-
const resolvedPath =
|
|
474
|
-
if (resolvedPath !== resolvedRoot && !resolvedPath.startsWith(resolvedRoot +
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
1364
|
+
var __dirname = path11.dirname(fileURLToPath(import.meta.url));
|
|
1355
1365
|
var require2 = createRequire(import.meta.url);
|
|
1356
|
-
var packageRoot = fs10.existsSync(
|
|
1366
|
+
var packageRoot = fs10.existsSync(path11.join(__dirname, "../package.json")) ? path11.resolve(__dirname, "..") : path11.resolve(__dirname, "../..");
|
|
1357
1367
|
function resolveInjectScript() {
|
|
1358
|
-
const compiledInject =
|
|
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 =
|
|
1363
|
-
const coreInject =
|
|
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 =
|
|
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 =
|
|
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-
|
|
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>
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
let selectionMode = false;
|
|
2
|
+
let highlightOverlay = null;
|
|
3
|
+
let tooltip = null;
|
|
4
|
+
let selectedOverlay = null;
|
|
5
|
+
let hoveredElement = null;
|
|
6
|
+
let selectedElement = null;
|
|
7
|
+
let selectedDomPath = null;
|
|
8
|
+
let overlayRafId = null;
|
|
9
|
+
const previewBackups = /* @__PURE__ */ new Map();
|
|
10
|
+
function createOverlays() {
|
|
11
|
+
highlightOverlay = document.createElement("div");
|
|
12
|
+
highlightOverlay.id = "tool-highlight";
|
|
13
|
+
Object.assign(highlightOverlay.style, {
|
|
14
|
+
position: "fixed",
|
|
15
|
+
pointerEvents: "none",
|
|
16
|
+
border: "2px solid #3b82f6",
|
|
17
|
+
backgroundColor: "rgba(59, 130, 246, 0.08)",
|
|
18
|
+
borderRadius: "2px",
|
|
19
|
+
zIndex: "99999",
|
|
20
|
+
display: "none",
|
|
21
|
+
transition: "all 0.1s ease"
|
|
22
|
+
});
|
|
23
|
+
document.body.appendChild(highlightOverlay);
|
|
24
|
+
tooltip = document.createElement("div");
|
|
25
|
+
tooltip.id = "tool-tooltip";
|
|
26
|
+
Object.assign(tooltip.style, {
|
|
27
|
+
position: "fixed",
|
|
28
|
+
pointerEvents: "none",
|
|
29
|
+
backgroundColor: "#1e1e2e",
|
|
30
|
+
color: "#cdd6f4",
|
|
31
|
+
padding: "3px 8px",
|
|
32
|
+
borderRadius: "4px",
|
|
33
|
+
fontSize: "11px",
|
|
34
|
+
fontFamily: "ui-monospace, monospace",
|
|
35
|
+
zIndex: "100000",
|
|
36
|
+
display: "none",
|
|
37
|
+
whiteSpace: "nowrap",
|
|
38
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.3)"
|
|
39
|
+
});
|
|
40
|
+
document.body.appendChild(tooltip);
|
|
41
|
+
selectedOverlay = document.createElement("div");
|
|
42
|
+
selectedOverlay.id = "tool-selected";
|
|
43
|
+
Object.assign(selectedOverlay.style, {
|
|
44
|
+
position: "fixed",
|
|
45
|
+
pointerEvents: "none",
|
|
46
|
+
border: "2px solid #f59e0b",
|
|
47
|
+
backgroundColor: "rgba(245, 158, 11, 0.06)",
|
|
48
|
+
borderRadius: "2px",
|
|
49
|
+
zIndex: "99998",
|
|
50
|
+
display: "none"
|
|
51
|
+
});
|
|
52
|
+
document.body.appendChild(selectedOverlay);
|
|
53
|
+
}
|
|
54
|
+
function getElementName(el) {
|
|
55
|
+
const slot = el.getAttribute("data-slot");
|
|
56
|
+
if (slot) {
|
|
57
|
+
return slot.charAt(0).toUpperCase() + slot.slice(1);
|
|
58
|
+
}
|
|
59
|
+
return `<${el.tagName.toLowerCase()}>`;
|
|
60
|
+
}
|
|
61
|
+
function getDomPath(el) {
|
|
62
|
+
const parts = [];
|
|
63
|
+
let current = el;
|
|
64
|
+
while (current && current !== document.body) {
|
|
65
|
+
let selector = current.tagName.toLowerCase();
|
|
66
|
+
const slot = current.getAttribute("data-slot");
|
|
67
|
+
if (slot) {
|
|
68
|
+
selector = `[data-slot="${slot}"]`;
|
|
69
|
+
} else if (current.id) {
|
|
70
|
+
selector += `#${current.id}`;
|
|
71
|
+
} else if (current.className && typeof current.className === "string") {
|
|
72
|
+
const cls = current.className.split(" ")[0];
|
|
73
|
+
if (cls) selector += `.${cls}`;
|
|
74
|
+
}
|
|
75
|
+
parts.unshift(selector);
|
|
76
|
+
current = current.parentElement;
|
|
77
|
+
}
|
|
78
|
+
return parts.join(" > ");
|
|
79
|
+
}
|
|
80
|
+
function extractElementData(el) {
|
|
81
|
+
const computed = getComputedStyle(el);
|
|
82
|
+
const rect = el.getBoundingClientRect();
|
|
83
|
+
const relevantProps = [
|
|
84
|
+
"color",
|
|
85
|
+
"backgroundColor",
|
|
86
|
+
"borderColor",
|
|
87
|
+
"borderRadius",
|
|
88
|
+
"padding",
|
|
89
|
+
"margin",
|
|
90
|
+
"gap",
|
|
91
|
+
"fontSize",
|
|
92
|
+
"fontWeight",
|
|
93
|
+
"lineHeight",
|
|
94
|
+
"letterSpacing",
|
|
95
|
+
"display",
|
|
96
|
+
"flexDirection",
|
|
97
|
+
"alignItems",
|
|
98
|
+
"justifyContent",
|
|
99
|
+
"width",
|
|
100
|
+
"height",
|
|
101
|
+
"boxShadow"
|
|
102
|
+
];
|
|
103
|
+
const computedStyles = {};
|
|
104
|
+
for (const prop of relevantProps) {
|
|
105
|
+
computedStyles[prop] = computed.getPropertyValue(
|
|
106
|
+
prop.replace(/([A-Z])/g, "-$1").toLowerCase()
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const attributes = {};
|
|
110
|
+
for (const attr of Array.from(el.attributes)) {
|
|
111
|
+
if (attr.name.startsWith("data-")) {
|
|
112
|
+
attributes[attr.name] = attr.value;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
tag: el.tagName.toLowerCase(),
|
|
117
|
+
className: (el.getAttribute("class") || "").trim(),
|
|
118
|
+
dataSlot: el.getAttribute("data-slot"),
|
|
119
|
+
dataVariant: el.getAttribute("data-variant"),
|
|
120
|
+
dataSize: el.getAttribute("data-size"),
|
|
121
|
+
computedStyles,
|
|
122
|
+
boundingRect: rect,
|
|
123
|
+
domPath: getDomPath(el),
|
|
124
|
+
textContent: (el.textContent || "").trim().slice(0, 100),
|
|
125
|
+
attributes
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function positionOverlay(overlay, rect) {
|
|
129
|
+
Object.assign(overlay.style, {
|
|
130
|
+
left: `${rect.left}px`,
|
|
131
|
+
top: `${rect.top}px`,
|
|
132
|
+
width: `${rect.width}px`,
|
|
133
|
+
height: `${rect.height}px`,
|
|
134
|
+
display: "block"
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function findSelectableElement(target) {
|
|
138
|
+
let el = target;
|
|
139
|
+
while (el && el !== document.body) {
|
|
140
|
+
if (el.getAttribute("data-slot")) return el;
|
|
141
|
+
el = el.parentElement;
|
|
142
|
+
}
|
|
143
|
+
return target;
|
|
144
|
+
}
|
|
145
|
+
function onMouseMove(e) {
|
|
146
|
+
if (!selectionMode || !highlightOverlay || !tooltip) return;
|
|
147
|
+
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
148
|
+
if (!el || el === highlightOverlay || el === tooltip || el === selectedOverlay) return;
|
|
149
|
+
const selectable = findSelectableElement(el);
|
|
150
|
+
if (selectable === hoveredElement) return;
|
|
151
|
+
hoveredElement = selectable;
|
|
152
|
+
const rect = selectable.getBoundingClientRect();
|
|
153
|
+
positionOverlay(highlightOverlay, rect);
|
|
154
|
+
const name = getElementName(selectable);
|
|
155
|
+
tooltip.textContent = name;
|
|
156
|
+
tooltip.style.display = "block";
|
|
157
|
+
tooltip.style.left = `${rect.left}px`;
|
|
158
|
+
tooltip.style.top = `${Math.max(0, rect.top - 24)}px`;
|
|
159
|
+
}
|
|
160
|
+
function onMouseLeave() {
|
|
161
|
+
if (!highlightOverlay || !tooltip) return;
|
|
162
|
+
highlightOverlay.style.display = "none";
|
|
163
|
+
tooltip.style.display = "none";
|
|
164
|
+
hoveredElement = null;
|
|
165
|
+
}
|
|
166
|
+
function onClick(e) {
|
|
167
|
+
if (!selectionMode) return;
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
e.stopPropagation();
|
|
170
|
+
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
171
|
+
if (!el || el === highlightOverlay || el === tooltip || el === selectedOverlay) return;
|
|
172
|
+
const selectable = findSelectableElement(el);
|
|
173
|
+
selectElement(selectable);
|
|
174
|
+
}
|
|
175
|
+
function selectElement(el) {
|
|
176
|
+
selectedElement = el;
|
|
177
|
+
selectedDomPath = getDomPath(el);
|
|
178
|
+
const data = extractElementData(el);
|
|
179
|
+
if (selectedOverlay) {
|
|
180
|
+
positionOverlay(selectedOverlay, data.boundingRect);
|
|
181
|
+
}
|
|
182
|
+
startOverlayTracking();
|
|
183
|
+
window.parent.postMessage(
|
|
184
|
+
{ type: "tool:elementSelected", data },
|
|
185
|
+
"*"
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
function reselectCurrentElement() {
|
|
189
|
+
if (!selectedDomPath) return;
|
|
190
|
+
const el = document.querySelector(selectedDomPath);
|
|
191
|
+
if (el) {
|
|
192
|
+
selectedElement = el;
|
|
193
|
+
const data = extractElementData(el);
|
|
194
|
+
if (selectedOverlay) {
|
|
195
|
+
positionOverlay(selectedOverlay, data.boundingRect);
|
|
196
|
+
}
|
|
197
|
+
window.parent.postMessage(
|
|
198
|
+
{ type: "tool:elementSelected", data },
|
|
199
|
+
"*"
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function startOverlayTracking() {
|
|
204
|
+
if (overlayRafId) cancelAnimationFrame(overlayRafId);
|
|
205
|
+
let lastRect = "";
|
|
206
|
+
function tick() {
|
|
207
|
+
if (selectedElement && selectedOverlay) {
|
|
208
|
+
if (!document.contains(selectedElement)) {
|
|
209
|
+
if (selectedDomPath) {
|
|
210
|
+
const newEl = document.querySelector(selectedDomPath);
|
|
211
|
+
if (newEl) {
|
|
212
|
+
selectedElement = newEl;
|
|
213
|
+
reselectCurrentElement();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (selectedElement && document.contains(selectedElement)) {
|
|
218
|
+
const rect = selectedElement.getBoundingClientRect();
|
|
219
|
+
const key = `${rect.left},${rect.top},${rect.width},${rect.height}`;
|
|
220
|
+
if (key !== lastRect) {
|
|
221
|
+
lastRect = key;
|
|
222
|
+
positionOverlay(selectedOverlay, rect);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
overlayRafId = requestAnimationFrame(tick);
|
|
227
|
+
}
|
|
228
|
+
tick();
|
|
229
|
+
}
|
|
230
|
+
function onMessage(e) {
|
|
231
|
+
const msg = e.data;
|
|
232
|
+
if (!msg || !msg.type || !msg.type.startsWith("tool:")) return;
|
|
233
|
+
switch (msg.type) {
|
|
234
|
+
case "tool:enterSelectionMode":
|
|
235
|
+
selectionMode = true;
|
|
236
|
+
document.body.style.cursor = "crosshair";
|
|
237
|
+
break;
|
|
238
|
+
case "tool:exitSelectionMode":
|
|
239
|
+
selectionMode = false;
|
|
240
|
+
document.body.style.cursor = "";
|
|
241
|
+
if (highlightOverlay) highlightOverlay.style.display = "none";
|
|
242
|
+
if (tooltip) tooltip.style.display = "none";
|
|
243
|
+
hoveredElement = null;
|
|
244
|
+
break;
|
|
245
|
+
case "tool:setProperty":
|
|
246
|
+
document.documentElement.style.setProperty(msg.token, msg.value);
|
|
247
|
+
break;
|
|
248
|
+
case "tool:previewShadow": {
|
|
249
|
+
let previewStyle = document.getElementById("tool-shadow-preview");
|
|
250
|
+
if (!previewStyle) {
|
|
251
|
+
previewStyle = document.createElement("style");
|
|
252
|
+
previewStyle.id = "tool-shadow-preview";
|
|
253
|
+
document.head.appendChild(previewStyle);
|
|
254
|
+
}
|
|
255
|
+
const cls = msg.className;
|
|
256
|
+
const val = msg.value;
|
|
257
|
+
if (val === "none" || val === "0 0 #0000") {
|
|
258
|
+
previewStyle.textContent = `.${CSS.escape(cls)} { --tw-shadow: 0 0 #0000 !important; box-shadow: none !important; }`;
|
|
259
|
+
} else {
|
|
260
|
+
previewStyle.textContent = `.${CSS.escape(cls)} { --tw-shadow: ${val} !important; }`;
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case "tool:previewClass": {
|
|
265
|
+
const target = document.querySelector(msg.elementPath);
|
|
266
|
+
if (target) {
|
|
267
|
+
if (!previewBackups.has(target)) {
|
|
268
|
+
previewBackups.set(target, target.getAttribute("class") || "");
|
|
269
|
+
}
|
|
270
|
+
const currentClass = target.getAttribute("class") || "";
|
|
271
|
+
target.setAttribute(
|
|
272
|
+
"class",
|
|
273
|
+
currentClass.replace(msg.oldClass, msg.newClass)
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case "tool:revertPreview":
|
|
279
|
+
for (const [el, backup] of previewBackups) {
|
|
280
|
+
el.setAttribute("class", backup);
|
|
281
|
+
}
|
|
282
|
+
previewBackups.clear();
|
|
283
|
+
break;
|
|
284
|
+
case "tool:reselectElement":
|
|
285
|
+
reselectCurrentElement();
|
|
286
|
+
break;
|
|
287
|
+
case "tool:setTheme":
|
|
288
|
+
if (msg.theme === "dark") {
|
|
289
|
+
document.documentElement.classList.add("dark");
|
|
290
|
+
} else {
|
|
291
|
+
document.documentElement.classList.remove("dark");
|
|
292
|
+
}
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function notifyPathChanged() {
|
|
297
|
+
const fullPath = window.location.pathname + window.location.search + window.location.hash;
|
|
298
|
+
const appPath = fullPath.startsWith("/proxy") ? fullPath.slice(6) || "/" : fullPath;
|
|
299
|
+
window.parent.postMessage({ type: "tool:pathChanged", path: appPath }, "*");
|
|
300
|
+
}
|
|
301
|
+
function interceptNavigation() {
|
|
302
|
+
document.addEventListener("click", (e) => {
|
|
303
|
+
if (selectionMode) return;
|
|
304
|
+
const anchor = e.target.closest("a");
|
|
305
|
+
if (!anchor) return;
|
|
306
|
+
const href = anchor.getAttribute("href");
|
|
307
|
+
if (!href) return;
|
|
308
|
+
if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("#") || href.startsWith("javascript:") || href.startsWith("/proxy/")) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (href.startsWith("/")) {
|
|
312
|
+
e.preventDefault();
|
|
313
|
+
window.location.href = `/proxy${href}`;
|
|
314
|
+
}
|
|
315
|
+
}, false);
|
|
316
|
+
window.addEventListener("popstate", () => notifyPathChanged());
|
|
317
|
+
notifyPathChanged();
|
|
318
|
+
}
|
|
319
|
+
function init() {
|
|
320
|
+
createOverlays();
|
|
321
|
+
interceptNavigation();
|
|
322
|
+
document.addEventListener("mousemove", onMouseMove, true);
|
|
323
|
+
document.addEventListener("mouseleave", onMouseLeave);
|
|
324
|
+
document.addEventListener("click", onClick, true);
|
|
325
|
+
window.addEventListener("message", onMessage);
|
|
326
|
+
window.parent.postMessage({ type: "tool:injectedReady" }, "*");
|
|
327
|
+
}
|
|
328
|
+
if (document.readyState === "loading") {
|
|
329
|
+
document.addEventListener("DOMContentLoaded", init);
|
|
330
|
+
} else {
|
|
331
|
+
init();
|
|
332
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@designtools/shadows",
|
|
3
|
-
"version": "0.1.
|
|
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
|
|
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",
|