dekit-cli 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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>Dekit Editor</title>
7
- <script type="module" crossorigin src="/assets/index-hySfUCIY.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-HLKs1tz-.css">
7
+ <script type="module" crossorigin src="/assets/index-E_PyZ4Xf.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-DKRQEtBb.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useEffect, useCallback, useRef } from "react";
2
- import type { EditorConfig, NodeInfo, DOMTreeNode, IframeMessage } from "./types.js";
2
+ import type { EditorConfig, NodeInfo, DOMTreeNode, IframeMessage, PropertyDef } from "./types.js";
3
3
  import { DEVICE_PRESETS, calcScale } from "./devices.js";
4
4
  import type { DevicePreset } from "./types.js";
5
5
  import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
@@ -26,8 +26,8 @@ export function App() {
26
26
  const [deviceWidth, setDeviceWidth] = useState(0);
27
27
  const [deviceHeight, setDeviceHeight] = useState(0);
28
28
  const [zoomMode, setZoomMode] = useState<"fit" | number>("fit");
29
- const [inspectMode, setInspectMode] = useState(false);
30
- const [inspectLocked, setInspectLocked] = useState(false);
29
+ const [inspectMode, setInspectMode] = useState(true);
30
+ const [inspectLocked, setInspectLocked] = useState(true);
31
31
  const inspectModeRef = useRef(false);
32
32
  const altPressedRef = useRef(false);
33
33
  const currentPageRef = useRef("");
@@ -35,6 +35,7 @@ export function App() {
35
35
  const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 });
36
36
  const [contextMenu, setContextMenu] = useState<{ x: number; y: number; cssSelector?: string; page: string } | null>(null);
37
37
  const [focusedPanel, setFocusedPanel] = useState<"pages" | "layers" | null>(null);
38
+ const [propsOverrides, setPropsOverrides] = useState<Record<string, Record<string, string>>>({});
38
39
 
39
40
  const configRef = useRef<EditorConfig | null>(null);
40
41
  configRef.current = config;
@@ -236,9 +237,19 @@ export function App() {
236
237
  return <div className="empty-state">Loading...</div>;
237
238
  }
238
239
 
239
- const iframeSrc = currentPage
240
- ? `${config.designServerUrl}/page/${currentPage}`
241
- : "";
240
+ const currentOverrides = currentPage ? propsOverrides[currentPage] : undefined;
241
+ let iframeSrc = "";
242
+ if (currentPage) {
243
+ const base = `${config.designServerUrl}/page/${currentPage}`;
244
+ const params = new URLSearchParams();
245
+ if (currentOverrides) {
246
+ for (const [key, value] of Object.entries(currentOverrides)) {
247
+ params.set(`props.${key}`, value);
248
+ }
249
+ }
250
+ const qs = params.toString();
251
+ iframeSrc = qs ? `${base}?${qs}` : base;
252
+ }
242
253
 
243
254
  return (
244
255
  <div className="editor-layout">
@@ -357,6 +368,30 @@ export function App() {
357
368
 
358
369
  {/* Right Panel */}
359
370
  <div className="right-panel">
371
+ {currentPage && config.pageProperties[currentPage] && Object.keys(config.pageProperties[currentPage]).length > 0 && (
372
+ <div className="right-panel-props">
373
+ <div className="panel-header">Page Props</div>
374
+ <OverlayScrollbarsComponent className="props-panel-scroll" defer>
375
+ <PagePropsPanel
376
+ properties={config.pageProperties[currentPage]}
377
+ overrides={propsOverrides[currentPage] ?? {}}
378
+ onChange={(key, value) => {
379
+ setPropsOverrides((prev) => ({
380
+ ...prev,
381
+ [currentPage]: { ...prev[currentPage], [key]: value },
382
+ }));
383
+ }}
384
+ onReset={() => {
385
+ setPropsOverrides((prev) => {
386
+ const next = { ...prev };
387
+ delete next[currentPage];
388
+ return next;
389
+ });
390
+ }}
391
+ />
392
+ </OverlayScrollbarsComponent>
393
+ </div>
394
+ )}
360
395
  <div className="right-panel-top">
361
396
  <div className="panel-header">Layers</div>
362
397
  <OverlayScrollbarsComponent className="layer-tree-scroll" defer>
@@ -743,3 +778,84 @@ function PropertyPanel({ node, nodeRef }: { node: NodeInfo; nodeRef: string }) {
743
778
  </>
744
779
  );
745
780
  }
781
+
782
+ function PagePropsPanel({
783
+ properties,
784
+ overrides,
785
+ onChange,
786
+ onReset,
787
+ }: {
788
+ properties: Record<string, PropertyDef>;
789
+ overrides: Record<string, string>;
790
+ onChange: (key: string, value: string) => void;
791
+ onReset: () => void;
792
+ }) {
793
+ const hasOverrides = Object.keys(overrides).length > 0;
794
+
795
+ return (
796
+ <div className="page-props-panel">
797
+ {Object.entries(properties).map(([key, def]) => {
798
+ const overridden = key in overrides;
799
+ const currentValue = overridden ? overrides[key] : String(def.default);
800
+
801
+ return (
802
+ <div key={key} className="property-row">
803
+ <span className="property-key">{key}</span>
804
+ {def.type === "boolean" ? (
805
+ <input
806
+ type="checkbox"
807
+ className="props-checkbox"
808
+ checked={currentValue === "true"}
809
+ onChange={(e) => onChange(key, String(e.target.checked))}
810
+ />
811
+ ) : (
812
+ <PropInput
813
+ type={def.type}
814
+ value={currentValue}
815
+ onChange={(v) => onChange(key, v)}
816
+ />
817
+ )}
818
+ </div>
819
+ );
820
+ })}
821
+ {hasOverrides && (
822
+ <button className="props-reset-btn" onClick={onReset}>
823
+ Reset
824
+ </button>
825
+ )}
826
+ </div>
827
+ );
828
+ }
829
+
830
+ function PropInput({
831
+ type,
832
+ value,
833
+ onChange,
834
+ }: {
835
+ type: "number" | "string";
836
+ value: string;
837
+ onChange: (value: string) => void;
838
+ }) {
839
+ const [draft, setDraft] = useState(value);
840
+
841
+ useEffect(() => {
842
+ setDraft(value);
843
+ }, [value]);
844
+
845
+ const commit = () => {
846
+ if (draft !== value) onChange(draft);
847
+ };
848
+
849
+ return (
850
+ <input
851
+ className="props-input"
852
+ type={type === "number" ? "number" : "text"}
853
+ value={draft}
854
+ onChange={(e) => setDraft(e.target.value)}
855
+ onBlur={commit}
856
+ onKeyDown={(e) => {
857
+ if (e.key === "Enter") (e.target as HTMLInputElement).blur();
858
+ }}
859
+ />
860
+ );
861
+ }
@@ -451,3 +451,70 @@ body {
451
451
  background: #094771;
452
452
  color: #ffffff;
453
453
  }
454
+
455
+ /* Page Props Panel */
456
+ .right-panel-props {
457
+ flex-shrink: 0;
458
+ display: flex;
459
+ flex-direction: column;
460
+ overflow: hidden;
461
+ border-bottom: 1px solid #4a4a4a;
462
+ max-height: 30%;
463
+ }
464
+
465
+ .right-panel-props .props-panel-scroll {
466
+ flex: 1;
467
+ }
468
+
469
+ .page-props-panel {
470
+ padding: 8px 12px;
471
+ }
472
+
473
+ .page-props-panel .property-row {
474
+ align-items: center;
475
+ }
476
+
477
+ .props-checkbox {
478
+ accent-color: #2563eb;
479
+ cursor: pointer;
480
+ }
481
+
482
+ .props-input {
483
+ background: transparent;
484
+ border: 1px solid #4a4a4a;
485
+ border-radius: 3px;
486
+ color: #ce9178;
487
+ padding: 2px 6px;
488
+ font-size: 12px;
489
+ width: 100px;
490
+ text-align: right;
491
+ outline: none;
492
+ }
493
+
494
+ .props-input:focus {
495
+ border-color: #2563eb;
496
+ color: #ffffff;
497
+ }
498
+
499
+ .props-input::-webkit-inner-spin-button,
500
+ .props-input::-webkit-outer-spin-button {
501
+ -webkit-appearance: none;
502
+ margin: 0;
503
+ }
504
+
505
+ .props-reset-btn {
506
+ margin-top: 8px;
507
+ width: 100%;
508
+ padding: 4px 0;
509
+ background: transparent;
510
+ border: 1px solid #4a4a4a;
511
+ border-radius: 3px;
512
+ color: #969696;
513
+ font-size: 11px;
514
+ cursor: pointer;
515
+ }
516
+
517
+ .props-reset-btn:hover {
518
+ border-color: #666;
519
+ color: #cccccc;
520
+ }
@@ -21,9 +21,15 @@ export interface DeviceConfig {
21
21
  name?: string;
22
22
  }
23
23
 
24
+ export interface PropertyDef {
25
+ type: "boolean" | "number" | "string";
26
+ default: unknown;
27
+ }
28
+
24
29
  export interface EditorConfig {
25
30
  pages: string[];
26
31
  pagePaths: Record<string, string>;
32
+ pageProperties: Record<string, Record<string, PropertyDef>>;
27
33
  components: string[];
28
34
  device: DeviceConfig | null;
29
35
  designServerUrl: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dekit-cli",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "A code-first design toolkit for agents and humans — create, preview, and screenshot HTML/CSS designs",
6
6
  "keywords": [
@@ -32,7 +32,7 @@
32
32
  "docs/usage.md"
33
33
  ],
34
34
  "engines": {
35
- "node": ">=18"
35
+ "node": ">=20"
36
36
  },
37
37
  "bin": {
38
38
  "dekit": "./bin/dekit.js"