floor-editor-ts 1.3.6 → 1.3.7

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.
@@ -7120,7 +7120,8 @@ var DEFAULT_CONFIG = {
7120
7120
  cameraThumbnail: "",
7121
7121
  cameraThumbnailOpacity: .65,
7122
7122
  canvasBackground: "#f8f9fa",
7123
- floorPlateBackground: "#f8f9fa"
7123
+ floorPlateBackground: "#f8f9fa",
7124
+ headerToolsMode: "full"
7124
7125
  };
7125
7126
  function normalizeConfig(value) {
7126
7127
  if (!value || typeof value !== "object") return { ...DEFAULT_CONFIG };
@@ -7138,6 +7139,7 @@ function normalizeConfig(value) {
7138
7139
  const opacity = typeof value.cameraThumbnailOpacity === "number" && value.cameraThumbnailOpacity >= 0 && value.cameraThumbnailOpacity <= 1 ? value.cameraThumbnailOpacity : DEFAULT_CONFIG.cameraThumbnailOpacity;
7139
7140
  const canvasBackground = normalizeColor$1(value.canvasBackground, DEFAULT_CONFIG.canvasBackground);
7140
7141
  const floorPlateBackground = normalizeColor$1(value.floorPlateBackground, DEFAULT_CONFIG.floorPlateBackground);
7142
+ const headerToolsMode = value.headerToolsMode === "compact" || value.headerToolsMode === "auto" || value.headerToolsMode === "full" ? value.headerToolsMode : DEFAULT_CONFIG.headerToolsMode;
7141
7143
  return {
7142
7144
  ...DEFAULT_CONFIG,
7143
7145
  ...value,
@@ -7146,7 +7148,8 @@ function normalizeConfig(value) {
7146
7148
  cameraThumbnail: image,
7147
7149
  cameraThumbnailOpacity: opacity,
7148
7150
  canvasBackground,
7149
- floorPlateBackground
7151
+ floorPlateBackground,
7152
+ headerToolsMode
7150
7153
  };
7151
7154
  }
7152
7155
  function normalizeColor$1(value, fallback) {
@@ -7155,16 +7158,31 @@ function normalizeColor$1(value, fallback) {
7155
7158
  if (/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(trimmed)) return trimmed;
7156
7159
  return fallback;
7157
7160
  }
7161
+ function isFloorLike(value) {
7162
+ return !!value && typeof value === "object" && typeof value.id === "string" && Array.isArray(value.walls);
7163
+ }
7164
+ /**
7165
+ * Accepts a full Project, `{ floors }`, a floors array, or a single Floor.
7166
+ * Furniture (incl. cameras) is preserved as given.
7167
+ */
7158
7168
  function normalizeFloorData(data) {
7159
7169
  if (!data || typeof data !== "object") return createDefaultProject();
7160
- if (!Array.isArray(data.floors) || data.floors.length === 0) return createDefaultProject();
7161
- if (!data.activeFloorId || !data.floors.some((f) => f.id === data.activeFloorId)) return {
7170
+ let floors;
7171
+ let rest = {};
7172
+ if (Array.isArray(data)) floors = data.filter(isFloorLike);
7173
+ else if (isFloorLike(data) && !Array.isArray(data.floors)) floors = [data];
7174
+ else {
7175
+ rest = data;
7176
+ floors = Array.isArray(rest.floors) ? rest.floors.filter(isFloorLike) : void 0;
7177
+ }
7178
+ if (!floors || floors.length === 0) return createDefaultProject();
7179
+ const activeFloorId = rest.activeFloorId && floors.some((f) => f.id === rest.activeFloorId) ? rest.activeFloorId : floors[0].id;
7180
+ return {
7162
7181
  ...createDefaultProject(),
7163
- ...data,
7164
- floors: data.floors,
7165
- activeFloorId: data.floors[0].id
7182
+ ...rest,
7183
+ floors,
7184
+ activeFloorId
7166
7185
  };
7167
- return data;
7168
7186
  }
7169
7187
  var RUNTIME_FURNITURE_FIELDS = [
7170
7188
  "showHeatmap",
@@ -56264,11 +56282,11 @@ var root_7$6 = /* @__PURE__ */ from_html(`<div class="flex items-center justify-
56264
56282
  before:content-[''] before:absolute before:w-4 before:h-4 before:rounded-full before:bg-white before:top-0.5 before:left-0.5 before:transition-transform checked:before:translate-x-5"/></label> <label class="flex items-center justify-between px-4 py-3.5 cursor-pointer"><span class="text-sm text-gray-700">Extension Lines</span> <input type="checkbox" class="w-10 h-5 rounded-full appearance-none cursor-pointer bg-gray-300 checked:bg-slate-700 relative transition-colors
56265
56283
  before:content-[''] before:absolute before:w-4 before:h-4 before:rounded-full before:bg-white before:top-0.5 before:left-0.5 before:transition-transform checked:before:translate-x-5"/></label> <label class="flex items-center justify-between px-4 py-3.5 cursor-pointer"><span class="text-sm text-gray-700">Object Distance</span> <input type="checkbox" class="w-10 h-5 rounded-full appearance-none cursor-pointer bg-gray-300 checked:bg-slate-700 relative transition-colors
56266
56284
  before:content-[''] before:absolute before:w-4 before:h-4 before:rounded-full before:bg-white before:top-0.5 before:left-0.5 before:transition-transform checked:before:translate-x-5"/></label> <div class="flex items-center justify-between px-4 py-3.5"><span class="text-sm text-gray-700">Line Color</span> <div class="flex items-center gap-2"><button style="background-color: #ffffff" aria-label="White line color"></button> <span class="text-gray-300">|</span> <button style="background-color: #1e293b" aria-label="Dark line color"></button></div></div></div>`, 1);
56267
- var root_9$2 = /* @__PURE__ */ from_html(`<button><span class="text-2xl"> </span> <span class="text-xs font-medium text-gray-700 dark:text-gray-300"> </span></button>`);
56285
+ var root_9$3 = /* @__PURE__ */ from_html(`<button><span class="text-2xl"> </span> <span class="text-xs font-medium text-gray-700 dark:text-gray-300"> </span></button>`);
56268
56286
  var root_8$3 = /* @__PURE__ */ from_html(`<div class="space-y-4"><div><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-3">Theme</span> <div class="flex gap-3"></div></div></div>`);
56269
- var root_11$2 = /* @__PURE__ */ from_html(`<button class="px-4 py-2 text-sm font-medium text-red-600 dark:text-red-400 border border-red-300 dark:border-red-600 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">Remove</button>`);
56287
+ var root_11$3 = /* @__PURE__ */ from_html(`<button class="px-4 py-2 text-sm font-medium text-red-600 dark:text-red-400 border border-red-300 dark:border-red-600 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">Remove</button>`);
56270
56288
  var root_12$3 = /* @__PURE__ */ from_html(`<button class="px-4 py-2 text-sm font-medium text-red-600 dark:text-red-400 border border-red-300 dark:border-red-600 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">Remove</button>`);
56271
- var root_10$2 = /* @__PURE__ */ from_html(`<div class="space-y-4"><div><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-1">Gemini API Key</span> <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">Required for AI-powered photorealistic rendering. Your key is stored locally in your browser only - never sent to our servers.</p> <div class="flex gap-2"><div class="relative flex-1"><input class="w-full px-3 py-2 pr-10 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-mono focus:ring-2 focus:ring-slate-500 focus:border-slate-500 outline-none bg-white dark:bg-gray-700 dark:text-gray-100" placeholder="AIza..."/> <button class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm"> </button></div></div> <div class="flex gap-2 mt-3"><button class="px-4 py-2 text-sm font-medium bg-slate-700 text-white rounded-lg hover:bg-slate-600 transition-colors"> </button> <!></div></div> <div class="border-t border-gray-200 dark:border-gray-700 pt-4"><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-2">How to get a Gemini key</span> <ol class="text-xs text-gray-500 dark:text-gray-400 space-y-1 list-decimal list-inside"><li>Go to <a href="https://aistudio.google.com/apikey" target="_blank" rel="noopener" class="text-blue-500 hover:underline">Google AI Studio</a></li> <li>Click "Create API Key"</li> <li>Copy and paste it above</li></ol></div> <div class="border-t border-gray-200 dark:border-gray-700 pt-4"><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-1">OpenAI API Key</span> <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">Optional — enables OpenAI image generation as an alternative to Gemini. Stored locally only.</p> <div class="flex gap-2"><div class="relative flex-1"><input class="w-full px-3 py-2 pr-10 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-mono focus:ring-2 focus:ring-slate-500 focus:border-slate-500 outline-none bg-white dark:bg-gray-700 dark:text-gray-100" placeholder="sk-..."/> <button class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm"> </button></div></div> <div class="flex gap-2 mt-3"><button class="px-4 py-2 text-sm font-medium bg-slate-700 text-white rounded-lg hover:bg-slate-600 transition-colors"> </button> <!></div> <div class="mt-3"><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-2">How to get an OpenAI key</span> <ol class="text-xs text-gray-500 dark:text-gray-400 space-y-1 list-decimal list-inside"><li>Go to <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener" class="text-blue-500 hover:underline">OpenAI Platform</a></li> <li>Click "Create new secret key"</li> <li>Copy and paste it above</li></ol></div></div></div>`);
56289
+ var root_10$3 = /* @__PURE__ */ from_html(`<div class="space-y-4"><div><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-1">Gemini API Key</span> <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">Required for AI-powered photorealistic rendering. Your key is stored locally in your browser only - never sent to our servers.</p> <div class="flex gap-2"><div class="relative flex-1"><input class="w-full px-3 py-2 pr-10 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-mono focus:ring-2 focus:ring-slate-500 focus:border-slate-500 outline-none bg-white dark:bg-gray-700 dark:text-gray-100" placeholder="AIza..."/> <button class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm"> </button></div></div> <div class="flex gap-2 mt-3"><button class="px-4 py-2 text-sm font-medium bg-slate-700 text-white rounded-lg hover:bg-slate-600 transition-colors"> </button> <!></div></div> <div class="border-t border-gray-200 dark:border-gray-700 pt-4"><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-2">How to get a Gemini key</span> <ol class="text-xs text-gray-500 dark:text-gray-400 space-y-1 list-decimal list-inside"><li>Go to <a href="https://aistudio.google.com/apikey" target="_blank" rel="noopener" class="text-blue-500 hover:underline">Google AI Studio</a></li> <li>Click "Create API Key"</li> <li>Copy and paste it above</li></ol></div> <div class="border-t border-gray-200 dark:border-gray-700 pt-4"><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-1">OpenAI API Key</span> <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">Optional — enables OpenAI image generation as an alternative to Gemini. Stored locally only.</p> <div class="flex gap-2"><div class="relative flex-1"><input class="w-full px-3 py-2 pr-10 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-mono focus:ring-2 focus:ring-slate-500 focus:border-slate-500 outline-none bg-white dark:bg-gray-700 dark:text-gray-100" placeholder="sk-..."/> <button class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm"> </button></div></div> <div class="flex gap-2 mt-3"><button class="px-4 py-2 text-sm font-medium bg-slate-700 text-white rounded-lg hover:bg-slate-600 transition-colors"> </button> <!></div> <div class="mt-3"><span class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-2">How to get an OpenAI key</span> <ol class="text-xs text-gray-500 dark:text-gray-400 space-y-1 list-decimal list-inside"><li>Go to <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener" class="text-blue-500 hover:underline">OpenAI Platform</a></li> <li>Click "Create new secret key"</li> <li>Copy and paste it above</li></ol></div></div></div>`);
56272
56290
  var root_1$13 = /* @__PURE__ */ from_html(`<div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center" role="dialog" tabindex="-1" aria-label="Settings"><div class="bg-white dark:bg-gray-800 rounded-xl shadow-2xl w-[420px] max-h-[80vh] flex flex-col" role="document"><div class="flex items-center justify-between px-5 pt-4 pb-2"><h2 class="text-lg font-bold text-gray-800 dark:text-gray-100">Settings</h2> <button class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-xl leading-none" aria-label="Close settings">✕</button></div> <div class="flex border-b border-gray-200 dark:border-gray-700 px-5"><button>Project <!></button> <button>Dimensions <!></button> <button>Appearance <!></button> <button>AI <!></button></div> <div class="p-5 overflow-y-auto"><!></div></div></div>`);
56273
56291
  function SettingsDialog($$anchor, $$props) {
56274
56292
  push($$props, true);
@@ -56519,7 +56537,7 @@ function SettingsDialog($$anchor, $$props) {
56519
56537
  let value = () => get($$array)[0];
56520
56538
  let icon = () => get($$array)[1];
56521
56539
  let label = () => get($$array)[2];
56522
- var button_9 = root_9$2();
56540
+ var button_9 = root_9$3();
56523
56541
  var span = child(button_9);
56524
56542
  var text = child(span, true);
56525
56543
  reset(span);
@@ -56541,7 +56559,7 @@ function SettingsDialog($$anchor, $$props) {
56541
56559
  append($$anchor, div_15);
56542
56560
  };
56543
56561
  var consequent_9 = ($$anchor) => {
56544
- var div_18 = root_10$2();
56562
+ var div_18 = root_10$3();
56545
56563
  var div_19 = child(div_18);
56546
56564
  var div_20 = sibling(child(div_19), 4);
56547
56565
  var div_21 = child(div_20);
@@ -56558,7 +56576,7 @@ function SettingsDialog($$anchor, $$props) {
56558
56576
  reset(button_11);
56559
56577
  var node_6 = sibling(button_11, 2);
56560
56578
  var consequent_7 = ($$anchor) => {
56561
- var button_12 = root_11$2();
56579
+ var button_12 = root_11$3();
56562
56580
  delegated("click", button_12, clearGeminiKey);
56563
56581
  append($$anchor, button_12);
56564
56582
  };
@@ -57129,14 +57147,19 @@ init_onboarding_svelte();
57129
57147
  var root_2$10 = /* @__PURE__ */ from_html(`<input type="text" class="bg-white/20 text-white font-semibold px-2 py-0.5 rounded border border-white/30 outline-none text-sm w-40"/>`);
57130
57148
  var root_3$9 = /* @__PURE__ */ from_html(`<button class="font-semibold text-white text-sm hover:bg-white/10 px-2 py-0.5 rounded transition-colors" title="Click to rename"> </button>`);
57131
57149
  var root_4$8 = /* @__PURE__ */ from_html(`<button> </button>`);
57132
- var root_5$5 = /* @__PURE__ */ from_html(`<div class="flex bg-white/15 rounded-full p-0.5"><button title="Select mode (V)" aria-label="Select mode"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path></svg></button> <button title="Pan mode (H)" aria-label="Pan mode"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 11V6a2 2 0 0 0-4 0v1"></path><path d="M14 10V4a2 2 0 0 0-4 0v2"></path><path d="M10 10.5V6a2 2 0 0 0-4 0v8"></path><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"></path></svg></button></div>`);
57133
- var root_6$4 = /* @__PURE__ */ from_html(`<div class="flex items-center gap-1 bg-white/15 rounded-full p-0.5"><button class="w-7 h-7 flex items-center justify-center text-white/80 hover:text-white hover:bg-white/10 rounded-full transition-colors text-sm font-bold" title="Zoom Out (−)" aria-label="Zoom Out">−</button> <button class="px-2 py-1 text-xs font-medium text-white/80 hover:text-white hover:bg-white/10 rounded-full transition-colors min-w-[3rem] text-center" title="Reset Zoom (100%)"> </button> <button class="w-7 h-7 flex items-center justify-center text-white/80 hover:text-white hover:bg-white/10 rounded-full transition-colors text-sm font-bold" title="Zoom In (+)" aria-label="Zoom In">+</button></div>`);
57134
- var root_8$2 = /* @__PURE__ */ from_html(`<button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg> Import JSON</button>`);
57135
- var root_7$5 = /* @__PURE__ */ from_html(`<div class="absolute right-0 top-full mt-1 bg-white rounded-lg shadow-lg border border-gray-200 py-1 w-48 z-50"><button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg> Print Layout</button> <div class="h-px bg-gray-100 my-1"></div> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><path d="M21 15l-5-5L5 21"></path></svg> Export 2D as PNG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"></path><path d="M2 17l10 5 10-5"></path><path d="M2 12l10 5 10-5"></path></svg> Export 3D as PNG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 19V5"></path><path d="M5 12l7-7 7 7"></path></svg> Export as SVG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M8 16h2"></path><path d="M14 16h2"></path></svg> Export as DXF</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M9 16h6"></path></svg> Export as DWG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M16 11v6"></path><path d="M8 11v6"></path><path d="M12 11v6"></path></svg> Export as PDF</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path></svg> Download JSON</button> <div class="h-px bg-gray-100 my-1"></div> <!> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg> New Project</button></div>`);
57136
- var root_1$10 = /* @__PURE__ */ from_html(`<div class="h-12 bg-gradient-to-r from-slate-800 to-slate-700 flex items-center px-4 gap-3 shrink-0 shadow-sm"><button type="button"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2.5"></rect><path d="M9 3v18"></path></svg></button> <!> <div class="h-5 w-px bg-white/20"></div> <div class="flex items-center gap-1"><!> <button class="text-white/80 hover:text-white text-xs hover:bg-white/10 px-1.5 py-0.5 rounded transition-colors" title="Add Floor" aria-label="Add Floor">+</button> <span class="text-white/40 text-[10px] ml-1"> </span></div> <div class="flex-1"></div> <button class="p-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Undo (Ctrl+Z)" aria-label="Undo"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg></button> <button class="p-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Redo (Ctrl+Y)" aria-label="Redo"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.13-9.36L23 10"></path></svg></button> <div class="h-5 w-px bg-white/20"></div> <button aria-label="Snap to Grid"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg></button> <!> <button aria-label="Toggle Furniture"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="12" width="20" height="8" rx="1"></rect><path d="M4 12V7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v5"></path><line x1="12" y1="12" x2="12" y2="20"></line></svg></button> <div class="h-5 w-px bg-white/20"></div> <!> <button class="p-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Version History" aria-label="Version History"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg></button> <button class="px-2 py-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Area Summary" aria-label="Area Summary"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"></rect><path d="M3 9h18"></path><path d="M9 3v18"></path></svg></button> <button class="px-2 py-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Settings" aria-label="Settings"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg></button> <div class="h-5 w-px bg-white/20"></div> <div class="relative"><button class="px-3 py-1.5 text-sm text-white/90 hover:text-white hover:bg-white/10 rounded transition-colors flex items-center gap-1.5"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg> Export</button> <!></div> <span><!></span> <button class="px-3 py-1.5 text-sm bg-white text-slate-800 font-semibold rounded-lg hover:bg-blue-50 transition-colors shadow-sm">Save</button></div>`);
57137
- var root_13$2 = /* @__PURE__ */ from_html(`<div class="absolute right-0 top-full mt-1 bg-white rounded-lg shadow-lg border border-gray-200 py-1 w-48 z-50"><button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg> Print Layout</button> <div class="h-px bg-gray-100 my-1"></div> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><path d="M21 15l-5-5L5 21"></path></svg> Export 2D as PNG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"></path><path d="M2 17l10 5 10-5"></path><path d="M2 12l10 5 10-5"></path></svg> Export 3D as PNG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 19V5"></path><path d="M5 12l7-7 7 7"></path></svg> Export as SVG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M8 16h2"></path><path d="M14 16h2"></path></svg> Export as DXF</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M9 16h6"></path></svg> Export as DWG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M16 11v6"></path><path d="M8 11v6"></path><path d="M12 11v6"></path></svg> Export as PDF</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path></svg> Download JSON</button> <div class="h-px bg-gray-100 my-1"></div> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg> Import JSON</button></div>`);
57138
- var root_12$2 = /* @__PURE__ */ from_html(`<div class="h-12 bg-gradient-to-r from-slate-800 to-slate-700 flex items-center justify-between px-4 gap-3 shrink-0 shadow-sm"><span class="font-semibold text-white text-sm px-2 py-0.5 rounded transition-colors"> </span> <div class="relative"><button class="px-3 py-1.5 text-sm text-white/90 hover:text-white hover:bg-white/10 rounded transition-colors flex items-center gap-1.5"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg> Import</button> <!></div></div>`);
57139
- var root_14$2 = /* @__PURE__ */ from_html(`<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40"><div class="bg-white rounded-xl shadow-2xl w-[420px] max-h-[80vh] overflow-hidden"><div class="flex items-center justify-between px-5 py-3 border-b border-gray-200"><h2 class="text-base font-semibold text-gray-800">📐 Area Summary</h2> <button class="text-gray-400 hover:text-gray-600 text-xl leading-none">&times;</button></div> <div class="overflow-y-auto max-h-[calc(80vh-52px)] p-1"><!></div></div></div>`);
57150
+ var root_5$5 = /* @__PURE__ */ from_html(`<button aria-label="Snap to Grid"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg></button>`);
57151
+ var root_6$4 = /* @__PURE__ */ from_html(`<div class="flex bg-white/15 rounded-full p-0.5 shrink-0"><button title="Select mode (V)" aria-label="Select mode"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path></svg></button> <button title="Pan mode (H)" aria-label="Pan mode"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 11V6a2 2 0 0 0-4 0v1"></path><path d="M14 10V4a2 2 0 0 0-4 0v2"></path><path d="M10 10.5V6a2 2 0 0 0-4 0v8"></path><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"></path></svg></button></div>`);
57152
+ var root_7$5 = /* @__PURE__ */ from_html(`<button aria-label="Toggle Furniture"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="12" width="20" height="8" rx="1"></rect><path d="M4 12V7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v5"></path><line x1="12" y1="12" x2="12" y2="20"></line></svg></button>`);
57153
+ var root_8$2 = /* @__PURE__ */ from_html(`<div class="flex items-center gap-1 bg-white/15 rounded-full p-0.5 shrink-0"><button class="w-7 h-7 flex items-center justify-center text-white/80 hover:text-white hover:bg-white/10 rounded-full transition-colors text-sm font-bold" title="Zoom Out (−)" aria-label="Zoom Out">−</button> <button class="px-2 py-1 text-xs font-medium text-white/80 hover:text-white hover:bg-white/10 rounded-full transition-colors min-w-[3rem] text-center" title="Reset Zoom (100%)"> </button> <button class="w-7 h-7 flex items-center justify-center text-white/80 hover:text-white hover:bg-white/10 rounded-full transition-colors text-sm font-bold" title="Zoom In (+)" aria-label="Zoom In">+</button></div>`);
57154
+ var root_10$2 = /* @__PURE__ */ from_html(`<div class="absolute right-0 top-full mt-1 bg-white rounded-lg shadow-lg border border-gray-200 py-1 w-56 z-50" role="menu"><button type="button" role="menuitemcheckbox" class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center justify-between gap-2"><span>Snap to grid</span> <span> </span></button> <button type="button" role="menuitemcheckbox" class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center justify-between gap-2"><span>Show furniture</span> <span> </span></button> <div class="h-px bg-gray-100 my-1"></div> <button type="button" role="menuitem" class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left">Version history</button> <button type="button" role="menuitem" class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left">Area summary</button> <div class="h-px bg-gray-100 my-1"></div> <button type="button" role="menuitem" class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left">Settings…</button></div>`);
57155
+ var root_9$2 = /* @__PURE__ */ from_html(`<div class="relative shrink-0"><button title="Settings &amp; tools" aria-label="Settings &amp; tools"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg></button> <!></div>`);
57156
+ var root_11$2 = /* @__PURE__ */ from_html(`<button class="p-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Version History" aria-label="Version History"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg></button> <button class="px-2 py-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Area Summary" aria-label="Area Summary"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"></rect><path d="M3 9h18"></path><path d="M9 3v18"></path></svg></button> <button class="px-2 py-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Settings" aria-label="Settings"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg></button>`, 1);
57157
+ var root_13$2 = /* @__PURE__ */ from_html(`<button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg> Import JSON</button>`);
57158
+ var root_12$2 = /* @__PURE__ */ from_html(`<div class="absolute right-0 top-full mt-1 bg-white rounded-lg shadow-lg border border-gray-200 py-1 w-48 z-50"><button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg> Print Layout</button> <div class="h-px bg-gray-100 my-1"></div> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><path d="M21 15l-5-5L5 21"></path></svg> Export 2D as PNG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"></path><path d="M2 17l10 5 10-5"></path><path d="M2 12l10 5 10-5"></path></svg> Export 3D as PNG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 19V5"></path><path d="M5 12l7-7 7 7"></path></svg> Export as SVG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M8 16h2"></path><path d="M14 16h2"></path></svg> Export as DXF</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M9 16h6"></path></svg> Export as DWG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M16 11v6"></path><path d="M8 11v6"></path><path d="M12 11v6"></path></svg> Export as PDF</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path></svg> Download JSON</button> <div class="h-px bg-gray-100 my-1"></div> <!> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg> New Project</button></div>`);
57159
+ var root_1$10 = /* @__PURE__ */ from_html(`<div class="h-12 bg-gradient-to-r from-slate-800 to-slate-700 flex items-center px-4 gap-3 shrink-0 shadow-sm overflow-hidden"><button type="button"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2.5"></rect><path d="M9 3v18"></path></svg></button> <!> <div class="h-5 w-px bg-white/20"></div> <div class="flex items-center gap-1"><!> <button class="text-white/80 hover:text-white text-xs hover:bg-white/10 px-1.5 py-0.5 rounded transition-colors" title="Add Floor" aria-label="Add Floor">+</button> <span class="text-white/40 text-[10px] ml-1"> </span></div> <div class="flex-1"></div> <button class="p-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Undo (Ctrl+Z)" aria-label="Undo"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg></button> <button class="p-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors" title="Redo (Ctrl+Y)" aria-label="Redo"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.13-9.36L23 10"></path></svg></button> <div class="h-5 w-px bg-white/20"></div> <!> <!> <!> <div class="h-5 w-px bg-white/20"></div> <!> <!> <div class="h-5 w-px bg-white/20 shrink-0"></div> <div class="relative"><button class="px-3 py-1.5 text-sm text-white/90 hover:text-white hover:bg-white/10 rounded transition-colors flex items-center gap-1.5"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg> Export</button> <!></div> <span><!></span> <button class="px-3 py-1.5 text-sm bg-white text-slate-800 font-semibold rounded-lg hover:bg-blue-50 transition-colors shadow-sm">Save</button></div>`);
57160
+ var root_18$3 = /* @__PURE__ */ from_html(`<div class="absolute right-0 top-full mt-1 bg-white rounded-lg shadow-lg border border-gray-200 py-1 w-48 z-50"><button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg> Print Layout</button> <div class="h-px bg-gray-100 my-1"></div> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><path d="M21 15l-5-5L5 21"></path></svg> Export 2D as PNG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"></path><path d="M2 17l10 5 10-5"></path><path d="M2 12l10 5 10-5"></path></svg> Export 3D as PNG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 19V5"></path><path d="M5 12l7-7 7 7"></path></svg> Export as SVG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M8 16h2"></path><path d="M14 16h2"></path></svg> Export as DXF</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M9 16h6"></path></svg> Export as DWG</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M16 11v6"></path><path d="M8 11v6"></path><path d="M12 11v6"></path></svg> Export as PDF</button> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path></svg> Download JSON</button> <div class="h-px bg-gray-100 my-1"></div> <button class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left flex items-center gap-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg> Import JSON</button></div>`);
57161
+ var root_17$3 = /* @__PURE__ */ from_html(`<div class="h-12 bg-gradient-to-r from-slate-800 to-slate-700 flex items-center justify-between px-4 gap-3 shrink-0 shadow-sm"><span class="font-semibold text-white text-sm px-2 py-0.5 rounded transition-colors"> </span> <div class="relative"><button class="px-3 py-1.5 text-sm text-white/90 hover:text-white hover:bg-white/10 rounded transition-colors flex items-center gap-1.5"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg> Import</button> <!></div></div>`);
57162
+ var root_19$3 = /* @__PURE__ */ from_html(`<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40"><div class="bg-white rounded-xl shadow-2xl w-[420px] max-h-[80vh] overflow-hidden"><div class="flex items-center justify-between px-5 py-3 border-b border-gray-200"><h2 class="text-base font-semibold text-gray-800">📐 Area Summary</h2> <button class="text-gray-400 hover:text-gray-600 text-xl leading-none">&times;</button></div> <div class="overflow-y-auto max-h-[calc(80vh-52px)] p-1"><!></div></div></div>`);
57140
57163
  var root$5 = /* @__PURE__ */ from_html(`<!> <!> <!> <!>`, 1);
57141
57164
  function TopBar($$anchor, $$props) {
57142
57165
  push($$props, true);
@@ -57145,10 +57168,17 @@ function TopBar($$anchor, $$props) {
57145
57168
  const $canvasZoom = () => store_get(canvasZoom, "$canvasZoom", $$stores);
57146
57169
  const $saveState = () => store_get(saveState, "$saveState", $$stores);
57147
57170
  const [$$stores, $$cleanup] = setup_stores();
57148
- let showBuildSidebar = prop($$props, "showBuildSidebar", 15, true);
57171
+ /** Tailwind / common `lg` breakpoint — below this, secondary tools fold into Settings. */
57172
+ const HEADER_AUTO_COMPACT_PX = 1024;
57173
+ let showBuildSidebar = prop($$props, "showBuildSidebar", 15, true), headerToolsMode = prop($$props, "headerToolsMode", 3, "full");
57149
57174
  let settingsOpen = /* @__PURE__ */ state(false);
57150
57175
  let areaOpen = /* @__PURE__ */ state(false);
57151
57176
  let versionHistoryOpen = /* @__PURE__ */ state(false);
57177
+ let toolsMenuOpen = /* @__PURE__ */ state(false);
57178
+ let headerNarrow = /* @__PURE__ */ state(false);
57179
+ let headerEl = /* @__PURE__ */ state(void 0);
57180
+ let toolsMenuRef = /* @__PURE__ */ state(void 0);
57181
+ const useCompactTools = /* @__PURE__ */ user_derived(() => headerToolsMode() === "compact" || headerToolsMode() === "auto" && get(headerNarrow));
57152
57182
  let projectName = /* @__PURE__ */ state("");
57153
57183
  let mode = /* @__PURE__ */ state("2d");
57154
57184
  let floors = /* @__PURE__ */ state(proxy([]));
@@ -57267,15 +57297,27 @@ function TopBar($$anchor, $$props) {
57267
57297
  currentProject.set(createDefaultProject());
57268
57298
  set(exportOpen, false);
57269
57299
  }
57300
+ function toggleSnap() {
57301
+ snapEnabled.update((v) => !v);
57302
+ set(snapOn, !get(snapOn));
57303
+ }
57304
+ function toggleFurniture() {
57305
+ layerVisibility.update((v) => ({
57306
+ ...v,
57307
+ furniture: !v.furniture
57308
+ }));
57309
+ }
57270
57310
  onMount(() => {
57271
57311
  initAutoSave($$props.onDispatch);
57272
57312
  initVersionHistory();
57273
57313
  const interval = setInterval(updateLastSavedText, 15e3);
57274
57314
  function handleClickOutside(e) {
57275
57315
  if (get(exportOpen) && exportRef && !exportRef.contains(e.target)) set(exportOpen, false);
57316
+ if (get(toolsMenuOpen) && get(toolsMenuRef) && !get(toolsMenuRef).contains(e.target)) set(toolsMenuOpen, false);
57276
57317
  }
57277
57318
  function handleKeydown(e) {
57278
57319
  if (get(exportOpen)) set(exportOpen, false);
57320
+ if (e.key === "Escape" && get(toolsMenuOpen)) set(toolsMenuOpen, false);
57279
57321
  if (e.key === "Escape" && get(versionHistoryOpen)) set(versionHistoryOpen, false);
57280
57322
  if (e.key === "Escape" && get(areaOpen)) set(areaOpen, false);
57281
57323
  }
@@ -57287,6 +57329,28 @@ function TopBar($$anchor, $$props) {
57287
57329
  clearInterval(interval);
57288
57330
  };
57289
57331
  });
57332
+ user_effect(() => {
57333
+ const el = get(headerEl);
57334
+ if (!el || headerToolsMode() !== "auto") {
57335
+ if (headerToolsMode() !== "auto") set(headerNarrow, false);
57336
+ return;
57337
+ }
57338
+ if (typeof ResizeObserver === "undefined") {
57339
+ set(headerNarrow, el.clientWidth < HEADER_AUTO_COMPACT_PX);
57340
+ return;
57341
+ }
57342
+ const ro = new ResizeObserver((entries) => {
57343
+ var _entries$0$contentRec, _entries$;
57344
+ const w = (_entries$0$contentRec = (_entries$ = entries[0]) === null || _entries$ === void 0 ? void 0 : _entries$.contentRect.width) !== null && _entries$0$contentRec !== void 0 ? _entries$0$contentRec : 0;
57345
+ set(headerNarrow, w > 0 && w < HEADER_AUTO_COMPACT_PX, true);
57346
+ });
57347
+ ro.observe(el);
57348
+ set(headerNarrow, el.clientWidth < HEADER_AUTO_COMPACT_PX);
57349
+ return () => ro.disconnect();
57350
+ });
57351
+ user_effect(() => {
57352
+ if (!get(useCompactTools) && get(toolsMenuOpen)) set(toolsMenuOpen, false);
57353
+ });
57290
57354
  function onImportJSON() {
57291
57355
  const input = document.createElement("input");
57292
57356
  input.type = "file";
@@ -57326,7 +57390,7 @@ function TopBar($$anchor, $$props) {
57326
57390
  }
57327
57391
  var fragment = root$5();
57328
57392
  var node = first_child(fragment);
57329
- var consequent_7 = ($$anchor) => {
57393
+ var consequent_11 = ($$anchor) => {
57330
57394
  var div = root_1$10();
57331
57395
  var button = child(div);
57332
57396
  var node_1 = sibling(button, 2);
@@ -57372,10 +57436,22 @@ function TopBar($$anchor, $$props) {
57372
57436
  reset(div_1);
57373
57437
  var button_4 = sibling(div_1, 4);
57374
57438
  var button_5 = sibling(button_4, 2);
57375
- var button_6 = sibling(button_5, 4);
57376
- var node_3 = sibling(button_6, 2);
57439
+ var node_3 = sibling(button_5, 4);
57377
57440
  var consequent_1 = ($$anchor) => {
57378
- var div_2 = root_5$5();
57441
+ var button_6 = root_5$5();
57442
+ template_effect(() => {
57443
+ set_class(button_6, 1, `p-1.5 rounded transition-colors ${get(snapOn) ? "text-white bg-white/20" : "text-white/40 hover:text-white/70 hover:bg-white/10"}`);
57444
+ set_attribute(button_6, "title", `Snap to Grid (${get(snapOn) ? "On" : "Off"})`);
57445
+ });
57446
+ delegated("click", button_6, toggleSnap);
57447
+ append($$anchor, button_6);
57448
+ };
57449
+ if_block(node_3, ($$render) => {
57450
+ if (!get(useCompactTools)) $$render(consequent_1);
57451
+ });
57452
+ var node_4 = sibling(node_3, 2);
57453
+ var consequent_2 = ($$anchor) => {
57454
+ var div_2 = root_6$4();
57379
57455
  var button_7 = child(div_2);
57380
57456
  var button_8 = sibling(button_7, 2);
57381
57457
  reset(div_2);
@@ -57387,13 +57463,25 @@ function TopBar($$anchor, $$props) {
57387
57463
  delegated("click", button_8, () => panMode.set(true));
57388
57464
  append($$anchor, div_2);
57389
57465
  };
57390
- if_block(node_3, ($$render) => {
57391
- if (get(mode) === "2d") $$render(consequent_1);
57466
+ if_block(node_4, ($$render) => {
57467
+ if (get(mode) === "2d") $$render(consequent_2);
57392
57468
  });
57393
- var button_9 = sibling(node_3, 2);
57394
- var node_4 = sibling(button_9, 4);
57395
- var consequent_2 = ($$anchor) => {
57396
- var div_3 = root_6$4();
57469
+ var node_5 = sibling(node_4, 2);
57470
+ var consequent_3 = ($$anchor) => {
57471
+ var button_9 = root_7$5();
57472
+ template_effect(() => {
57473
+ set_class(button_9, 1, `p-1.5 rounded transition-colors ${$showFurnitureStore() ? "text-white bg-white/20" : "text-white/40 hover:text-white/70 hover:bg-white/10"}`);
57474
+ set_attribute(button_9, "title", `Toggle Furniture (${$showFurnitureStore() ? "Visible" : "Hidden"})`);
57475
+ });
57476
+ delegated("click", button_9, toggleFurniture);
57477
+ append($$anchor, button_9);
57478
+ };
57479
+ if_block(node_5, ($$render) => {
57480
+ if (!get(useCompactTools)) $$render(consequent_3);
57481
+ });
57482
+ var node_6 = sibling(node_5, 4);
57483
+ var consequent_4 = ($$anchor) => {
57484
+ var div_3 = root_8$2();
57397
57485
  var button_10 = child(div_3);
57398
57486
  var button_11 = sibling(button_10, 2);
57399
57487
  var text_4 = child(button_11);
@@ -57406,77 +57494,146 @@ function TopBar($$anchor, $$props) {
57406
57494
  delegated("click", button_12, () => canvasZoom.update((z) => Math.min(10, z * 1.25)));
57407
57495
  append($$anchor, div_3);
57408
57496
  };
57409
- if_block(node_4, ($$render) => {
57410
- if (get(mode) === "2d") $$render(consequent_2);
57497
+ if_block(node_6, ($$render) => {
57498
+ if (get(mode) === "2d") $$render(consequent_4);
57411
57499
  });
57412
- var button_13 = sibling(node_4, 2);
57413
- var button_14 = sibling(button_13, 2);
57414
- var button_15 = sibling(button_14, 2);
57415
- var div_4 = sibling(button_15, 4);
57416
- var button_16 = child(div_4);
57417
- var node_5 = sibling(button_16, 2);
57418
- var consequent_4 = ($$anchor) => {
57419
- var div_5 = root_7$5();
57420
- var button_17 = child(div_5);
57421
- var button_18 = sibling(button_17, 4);
57422
- var button_19 = sibling(button_18, 2);
57500
+ var node_7 = sibling(node_6, 2);
57501
+ var consequent_6 = ($$anchor) => {
57502
+ var div_4 = root_9$2();
57503
+ var button_13 = child(div_4);
57504
+ var node_8 = sibling(button_13, 2);
57505
+ var consequent_5 = ($$anchor) => {
57506
+ var div_5 = root_10$2();
57507
+ var button_14 = child(div_5);
57508
+ var span_1 = sibling(child(button_14), 2);
57509
+ var text_5 = child(span_1, true);
57510
+ reset(span_1);
57511
+ reset(button_14);
57512
+ var button_15 = sibling(button_14, 2);
57513
+ var span_2 = sibling(child(button_15), 2);
57514
+ var text_6 = child(span_2, true);
57515
+ reset(span_2);
57516
+ reset(button_15);
57517
+ var button_16 = sibling(button_15, 4);
57518
+ var button_17 = sibling(button_16, 2);
57519
+ var button_18 = sibling(button_17, 4);
57520
+ reset(div_5);
57521
+ template_effect(() => {
57522
+ set_attribute(button_14, "aria-checked", get(snapOn));
57523
+ set_class(span_1, 1, `text-xs font-medium ${get(snapOn) ? "text-emerald-600" : "text-gray-400"}`);
57524
+ set_text(text_5, get(snapOn) ? "On" : "Off");
57525
+ set_attribute(button_15, "aria-checked", $showFurnitureStore());
57526
+ set_class(span_2, 1, `text-xs font-medium ${$showFurnitureStore() ? "text-emerald-600" : "text-gray-400"}`);
57527
+ set_text(text_6, $showFurnitureStore() ? "On" : "Off");
57528
+ });
57529
+ delegated("click", button_14, toggleSnap);
57530
+ delegated("click", button_15, toggleFurniture);
57531
+ delegated("click", button_16, () => {
57532
+ set(toolsMenuOpen, false);
57533
+ set(versionHistoryOpen, true);
57534
+ });
57535
+ delegated("click", button_17, () => {
57536
+ set(toolsMenuOpen, false);
57537
+ set(areaOpen, true);
57538
+ });
57539
+ delegated("click", button_18, () => {
57540
+ set(toolsMenuOpen, false);
57541
+ set(settingsOpen, true);
57542
+ });
57543
+ append($$anchor, div_5);
57544
+ };
57545
+ if_block(node_8, ($$render) => {
57546
+ if (get(toolsMenuOpen)) $$render(consequent_5);
57547
+ });
57548
+ reset(div_4);
57549
+ bind_this(div_4, ($$value) => set(toolsMenuRef, $$value), () => get(toolsMenuRef));
57550
+ template_effect(() => {
57551
+ set_class(button_13, 1, `px-2 py-1.5 text-white/80 hover:text-white hover:bg-white/10 rounded transition-colors ${get(toolsMenuOpen) ? "bg-white/15 text-white" : ""}`);
57552
+ set_attribute(button_13, "aria-expanded", get(toolsMenuOpen));
57553
+ });
57554
+ delegated("click", button_13, () => set(toolsMenuOpen, !get(toolsMenuOpen)));
57555
+ append($$anchor, div_4);
57556
+ };
57557
+ var alternate_1 = ($$anchor) => {
57558
+ var fragment_1 = root_11$2();
57559
+ var button_19 = first_child(fragment_1);
57423
57560
  var button_20 = sibling(button_19, 2);
57424
57561
  var button_21 = sibling(button_20, 2);
57425
- var button_22 = sibling(button_21, 2);
57426
- var button_23 = sibling(button_22, 2);
57427
- var button_24 = sibling(button_23, 2);
57428
- var node_6 = sibling(button_24, 4);
57429
- var consequent_3 = ($$anchor) => {
57430
- var button_25 = root_8$2();
57431
- delegated("click", button_25, onImportJSON);
57432
- append($$anchor, button_25);
57562
+ delegated("click", button_19, () => set(versionHistoryOpen, true));
57563
+ delegated("click", button_20, () => set(areaOpen, true));
57564
+ delegated("click", button_21, () => set(settingsOpen, true));
57565
+ append($$anchor, fragment_1);
57566
+ };
57567
+ if_block(node_7, ($$render) => {
57568
+ if (get(useCompactTools)) $$render(consequent_6);
57569
+ else $$render(alternate_1, -1);
57570
+ });
57571
+ var div_6 = sibling(node_7, 4);
57572
+ var button_22 = child(div_6);
57573
+ var node_9 = sibling(button_22, 2);
57574
+ var consequent_8 = ($$anchor) => {
57575
+ var div_7 = root_12$2();
57576
+ var button_23 = child(div_7);
57577
+ var button_24 = sibling(button_23, 4);
57578
+ var button_25 = sibling(button_24, 2);
57579
+ var button_26 = sibling(button_25, 2);
57580
+ var button_27 = sibling(button_26, 2);
57581
+ var button_28 = sibling(button_27, 2);
57582
+ var button_29 = sibling(button_28, 2);
57583
+ var button_30 = sibling(button_29, 2);
57584
+ var node_10 = sibling(button_30, 4);
57585
+ var consequent_7 = ($$anchor) => {
57586
+ var button_31 = root_13$2();
57587
+ delegated("click", button_31, onImportJSON);
57588
+ append($$anchor, button_31);
57433
57589
  };
57434
- if_block(node_6, ($$render) => {
57435
- if (!$$props.viewOnly) $$render(consequent_3);
57590
+ if_block(node_10, ($$render) => {
57591
+ if (!$$props.viewOnly) $$render(consequent_7);
57436
57592
  });
57437
- var button_26 = sibling(node_6, 2);
57438
- reset(div_5);
57439
- delegated("click", button_17, () => {
57593
+ var button_32 = sibling(node_10, 2);
57594
+ reset(div_7);
57595
+ delegated("click", button_23, () => {
57440
57596
  set(exportOpen, false);
57441
57597
  window.dispatchEvent(new KeyboardEvent("keydown", {
57442
57598
  key: "p",
57443
57599
  ctrlKey: true
57444
57600
  }));
57445
57601
  });
57446
- delegated("click", button_18, onExport2DPNG);
57447
- delegated("click", button_19, onExport3DPNG);
57448
- delegated("click", button_20, onExportSVG);
57449
- delegated("click", button_21, onExportDXF);
57450
- delegated("click", button_22, onExportDWG);
57451
- delegated("click", button_23, onExportPDF);
57452
- delegated("click", button_24, onExportJSON);
57453
- delegated("click", button_26, newProject);
57454
- append($$anchor, div_5);
57602
+ delegated("click", button_24, onExport2DPNG);
57603
+ delegated("click", button_25, onExport3DPNG);
57604
+ delegated("click", button_26, onExportSVG);
57605
+ delegated("click", button_27, onExportDXF);
57606
+ delegated("click", button_28, onExportDWG);
57607
+ delegated("click", button_29, onExportPDF);
57608
+ delegated("click", button_30, onExportJSON);
57609
+ delegated("click", button_32, newProject);
57610
+ append($$anchor, div_7);
57455
57611
  };
57456
- if_block(node_5, ($$render) => {
57457
- if (get(exportOpen)) $$render(consequent_4);
57612
+ if_block(node_9, ($$render) => {
57613
+ if (get(exportOpen)) $$render(consequent_8);
57458
57614
  });
57459
- reset(div_4);
57460
- bind_this(div_4, ($$value) => exportRef = $$value, () => exportRef);
57461
- var span_1 = sibling(div_4, 2);
57462
- var node_7 = child(span_1);
57463
- var consequent_5 = ($$anchor) => {
57615
+ reset(div_6);
57616
+ bind_this(div_6, ($$value) => exportRef = $$value, () => exportRef);
57617
+ var span_3 = sibling(div_6, 2);
57618
+ var node_11 = child(span_3);
57619
+ var consequent_9 = ($$anchor) => {
57464
57620
  append($$anchor, text$1("Saving…"));
57465
57621
  };
57466
- var consequent_6 = ($$anchor) => {
57622
+ var consequent_10 = ($$anchor) => {
57467
57623
  append($$anchor, text$1("Saved ✓"));
57468
57624
  };
57469
- var alternate_1 = ($$anchor) => {
57625
+ var alternate_2 = ($$anchor) => {
57470
57626
  append($$anchor, text$1("Unsaved •"));
57471
57627
  };
57472
- if_block(node_7, ($$render) => {
57473
- if ($saveState() === "saving") $$render(consequent_5);
57474
- else if ($saveState() === "saved") $$render(consequent_6, 1);
57475
- else $$render(alternate_1, -1);
57628
+ if_block(node_11, ($$render) => {
57629
+ if ($saveState() === "saving") $$render(consequent_9);
57630
+ else if ($saveState() === "saved") $$render(consequent_10, 1);
57631
+ else $$render(alternate_2, -1);
57476
57632
  });
57477
- reset(span_1);
57478
- var button_27 = sibling(span_1, 2);
57633
+ reset(span_3);
57634
+ var button_33 = sibling(span_3, 2);
57479
57635
  reset(div);
57636
+ bind_this(div, ($$value) => set(headerEl, $$value), () => get(headerEl));
57480
57637
  template_effect(() => {
57481
57638
  var _$$get$length;
57482
57639
  set_class(button, 1, `shrink-0 w-8 h-8 rounded-md flex items-center justify-center text-white/80 hover:text-white hover:bg-white/10 transition-colors ${showBuildSidebar() ? "bg-white/15 text-white" : ""}`);
@@ -57484,12 +57641,8 @@ function TopBar($$anchor, $$props) {
57484
57641
  set_attribute(button, "aria-label", showBuildSidebar() ? "Hide sidebar" : "Show sidebar");
57485
57642
  set_attribute(button, "aria-pressed", showBuildSidebar());
57486
57643
  set_text(text_3, `${(_$$get$length = get(floors).length) !== null && _$$get$length !== void 0 ? _$$get$length : ""}F`);
57487
- set_class(button_6, 1, `p-1.5 rounded transition-colors ${get(snapOn) ? "text-white bg-white/20" : "text-white/40 hover:text-white/70 hover:bg-white/10"}`);
57488
- set_attribute(button_6, "title", `Snap to Grid (${get(snapOn) ? "On" : "Off"})`);
57489
- set_class(button_9, 1, `p-1.5 rounded transition-colors ${$showFurnitureStore() ? "text-white bg-white/20" : "text-white/40 hover:text-white/70 hover:bg-white/10"}`);
57490
- set_attribute(button_9, "title", `Toggle Furniture (${$showFurnitureStore() ? "Visible" : "Hidden"})`);
57491
- set_class(span_1, 1, `text-[11px] font-medium transition-all duration-300 ${$saveState() === "saved" ? "text-emerald-400" : $saveState() === "saving" ? "text-amber-300 animate-pulse" : "text-white/50"}`);
57492
- set_attribute(span_1, "title", get(lastSavedText) || "Not saved yet");
57644
+ set_class(span_3, 1, `text-[11px] font-medium transition-all duration-300 ${$saveState() === "saved" ? "text-emerald-400" : $saveState() === "saving" ? "text-amber-300 animate-pulse" : "text-white/50"}`);
57645
+ set_attribute(span_3, "title", get(lastSavedText) || "Not saved yet");
57493
57646
  });
57494
57647
  delegated("click", button, () => showBuildSidebar(!showBuildSidebar()));
57495
57648
  delegated("click", button_3, onAddFloor);
@@ -57499,80 +57652,69 @@ function TopBar($$anchor, $$props) {
57499
57652
  delegated("click", button_5, function(...$$args) {
57500
57653
  redo === null || redo === void 0 || redo.apply(this, $$args);
57501
57654
  });
57502
- delegated("click", button_6, () => {
57503
- snapEnabled.update((v) => !v);
57504
- set(snapOn, !get(snapOn));
57505
- });
57506
- delegated("click", button_9, () => layerVisibility.update((v) => ({
57507
- ...v,
57508
- furniture: !v.furniture
57509
- })));
57510
- delegated("click", button_13, () => set(versionHistoryOpen, true));
57511
- delegated("click", button_14, () => set(areaOpen, true));
57512
- delegated("click", button_15, () => set(settingsOpen, true));
57513
- delegated("click", button_16, () => {
57655
+ delegated("click", button_22, () => {
57514
57656
  set(exportOpen, !get(exportOpen));
57515
57657
  if (get(exportOpen)) triggerTip("first-export", 300, 60);
57516
57658
  });
57517
- delegated("click", button_27, save);
57659
+ delegated("click", button_33, save);
57518
57660
  append($$anchor, div);
57519
57661
  };
57520
- var alternate_2 = ($$anchor) => {
57521
- var div_6 = root_12$2();
57522
- var span_2 = child(div_6);
57523
- var text_8 = child(span_2, true);
57524
- reset(span_2);
57525
- var div_7 = sibling(span_2, 2);
57526
- var button_28 = child(div_7);
57527
- var node_8 = sibling(button_28, 2);
57528
- var consequent_8 = ($$anchor) => {
57529
- var div_8 = root_13$2();
57530
- var button_29 = child(div_8);
57531
- var button_30 = sibling(button_29, 4);
57532
- var button_31 = sibling(button_30, 2);
57533
- var button_32 = sibling(button_31, 2);
57534
- var button_33 = sibling(button_32, 2);
57535
- var button_34 = sibling(button_33, 2);
57536
- var button_35 = sibling(button_34, 2);
57537
- var button_36 = sibling(button_35, 2);
57538
- var button_37 = sibling(button_36, 4);
57539
- reset(div_8);
57540
- delegated("click", button_29, () => {
57662
+ var alternate_3 = ($$anchor) => {
57663
+ var div_8 = root_17$3();
57664
+ var span_4 = child(div_8);
57665
+ var text_10 = child(span_4, true);
57666
+ reset(span_4);
57667
+ var div_9 = sibling(span_4, 2);
57668
+ var button_34 = child(div_9);
57669
+ var node_12 = sibling(button_34, 2);
57670
+ var consequent_12 = ($$anchor) => {
57671
+ var div_10 = root_18$3();
57672
+ var button_35 = child(div_10);
57673
+ var button_36 = sibling(button_35, 4);
57674
+ var button_37 = sibling(button_36, 2);
57675
+ var button_38 = sibling(button_37, 2);
57676
+ var button_39 = sibling(button_38, 2);
57677
+ var button_40 = sibling(button_39, 2);
57678
+ var button_41 = sibling(button_40, 2);
57679
+ var button_42 = sibling(button_41, 2);
57680
+ var button_43 = sibling(button_42, 4);
57681
+ reset(div_10);
57682
+ delegated("click", button_35, () => {
57541
57683
  set(exportOpen, false);
57542
57684
  window.dispatchEvent(new KeyboardEvent("keydown", {
57543
57685
  key: "p",
57544
57686
  ctrlKey: true
57545
57687
  }));
57546
57688
  });
57547
- delegated("click", button_30, onExport2DPNG);
57548
- delegated("click", button_31, onExport3DPNG);
57549
- delegated("click", button_32, onExportSVG);
57550
- delegated("click", button_33, onExportDXF);
57551
- delegated("click", button_34, onExportDWG);
57552
- delegated("click", button_35, onExportPDF);
57553
- delegated("click", button_36, onExportJSON);
57554
- delegated("click", button_37, onImportJSON);
57555
- append($$anchor, div_8);
57689
+ delegated("click", button_36, onExport2DPNG);
57690
+ delegated("click", button_37, onExport3DPNG);
57691
+ delegated("click", button_38, onExportSVG);
57692
+ delegated("click", button_39, onExportDXF);
57693
+ delegated("click", button_40, onExportDWG);
57694
+ delegated("click", button_41, onExportPDF);
57695
+ delegated("click", button_42, onExportJSON);
57696
+ delegated("click", button_43, onImportJSON);
57697
+ append($$anchor, div_10);
57556
57698
  };
57557
- if_block(node_8, ($$render) => {
57558
- if (get(exportOpen)) $$render(consequent_8);
57699
+ if_block(node_12, ($$render) => {
57700
+ if (get(exportOpen)) $$render(consequent_12);
57559
57701
  });
57560
- reset(div_7);
57561
- bind_this(div_7, ($$value) => exportRef = $$value, () => exportRef);
57562
- reset(div_6);
57563
- template_effect(() => set_text(text_8, get(projectName)));
57564
- delegated("click", button_28, () => {
57702
+ reset(div_9);
57703
+ bind_this(div_9, ($$value) => exportRef = $$value, () => exportRef);
57704
+ reset(div_8);
57705
+ template_effect(() => set_text(text_10, get(projectName)));
57706
+ delegated("click", button_34, () => {
57565
57707
  set(exportOpen, !get(exportOpen));
57566
57708
  if (get(exportOpen)) triggerTip("first-export", 300, 60);
57567
57709
  });
57568
- append($$anchor, div_6);
57710
+ append($$anchor, div_8);
57569
57711
  };
57570
57712
  if_block(node, ($$render) => {
57571
- if (!$$props.viewOnly) $$render(consequent_7);
57572
- else $$render(alternate_2, -1);
57713
+ if (!$$props.viewOnly) $$render(consequent_11);
57714
+ else $$render(alternate_3, -1);
57573
57715
  });
57574
- var node_9 = sibling(node, 2);
57575
- SettingsDialog(node_9, {
57716
+ var node_13 = sibling(node, 2);
57717
+ SettingsDialog(node_13, {
57576
57718
  get open() {
57577
57719
  return get(settingsOpen);
57578
57720
  },
@@ -57580,8 +57722,8 @@ function TopBar($$anchor, $$props) {
57580
57722
  set(settingsOpen, $$value, true);
57581
57723
  }
57582
57724
  });
57583
- var node_10 = sibling(node_9, 2);
57584
- VersionHistoryPanel(node_10, {
57725
+ var node_14 = sibling(node_13, 2);
57726
+ VersionHistoryPanel(node_14, {
57585
57727
  get open() {
57586
57728
  return get(versionHistoryOpen);
57587
57729
  },
@@ -57589,28 +57731,28 @@ function TopBar($$anchor, $$props) {
57589
57731
  set(versionHistoryOpen, $$value, true);
57590
57732
  }
57591
57733
  });
57592
- var node_11 = sibling(node_10, 2);
57593
- var consequent_9 = ($$anchor) => {
57594
- var div_9 = root_14$2();
57595
- var div_10 = child(div_9);
57596
- var div_11 = child(div_10);
57597
- var button_38 = sibling(child(div_11), 2);
57598
- reset(div_11);
57599
- var div_12 = sibling(div_11, 2);
57600
- AreaSummaryPanel(child(div_12), {});
57734
+ var node_15 = sibling(node_14, 2);
57735
+ var consequent_13 = ($$anchor) => {
57736
+ var div_11 = root_19$3();
57737
+ var div_12 = child(div_11);
57738
+ var div_13 = child(div_12);
57739
+ var button_44 = sibling(child(div_13), 2);
57740
+ reset(div_13);
57741
+ var div_14 = sibling(div_13, 2);
57742
+ AreaSummaryPanel(child(div_14), {});
57743
+ reset(div_14);
57601
57744
  reset(div_12);
57602
- reset(div_10);
57603
- reset(div_9);
57604
- delegated("click", div_9, () => set(areaOpen, false));
57605
- delegated("keydown", div_9, (e) => {
57745
+ reset(div_11);
57746
+ delegated("click", div_11, () => set(areaOpen, false));
57747
+ delegated("keydown", div_11, (e) => {
57606
57748
  if (e.key === "Escape") set(areaOpen, false);
57607
57749
  });
57608
- delegated("click", div_10, (e) => e.stopPropagation());
57609
- delegated("click", button_38, () => set(areaOpen, false));
57610
- append($$anchor, div_9);
57750
+ delegated("click", div_12, (e) => e.stopPropagation());
57751
+ delegated("click", button_44, () => set(areaOpen, false));
57752
+ append($$anchor, div_11);
57611
57753
  };
57612
- if_block(node_11, ($$render) => {
57613
- if (get(areaOpen)) $$render(consequent_9);
57754
+ if_block(node_15, ($$render) => {
57755
+ if (get(areaOpen)) $$render(consequent_13);
57614
57756
  });
57615
57757
  append($$anchor, fragment);
57616
57758
  pop();
@@ -66756,13 +66898,19 @@ function FloorPlanCanvas($$anchor, $$props) {
66756
66898
  markDirty();
66757
66899
  });
66758
66900
  const unsubZoom = canvasZoom.subscribe((v) => {
66901
+ if (get(zoom) === v) return;
66759
66902
  set(zoom, v, true);
66903
+ markDirty();
66760
66904
  });
66761
66905
  const unsubCamX = canvasCamX.subscribe((v) => {
66906
+ if (get(camX) === v) return;
66762
66907
  set(camX, v, true);
66908
+ markDirty();
66763
66909
  });
66764
66910
  const unsubCamY = canvasCamY.subscribe((v) => {
66911
+ if (get(camY) === v) return;
66765
66912
  set(camY, v, true);
66913
+ markDirty();
66766
66914
  });
66767
66915
  function handlePaste(e) {
66768
66916
  if (!e.clipboardData) return;
@@ -68961,12 +69109,15 @@ function FloorPlanCanvas($$anchor, $$props) {
68961
69109
  set(camX, worldCX - (get(width) / 2 - get(width) / 2) / newZoom);
68962
69110
  set(camY, worldCY - (get(height) / 2 - get(height) / 2) / newZoom);
68963
69111
  set(zoom, newZoom, true);
69112
+ markDirty();
68964
69113
  });
68965
69114
  delegated("click", button_12, () => {
68966
69115
  set(zoom, 1);
69116
+ markDirty();
68967
69117
  });
68968
69118
  delegated("click", button_13, () => {
68969
69119
  set(zoom, Math.min(10, get(zoom) * 1.25), true);
69120
+ markDirty();
68970
69121
  });
68971
69122
  delegated("click", button_14, () => zoomToFit());
68972
69123
  append($$anchor, fragment);
@@ -69968,6 +70119,17 @@ var CAMERA_SPACING = 40;
69968
70119
  var CAMERA_HIT = 28;
69969
70120
  /** Avoid spamming host with the same rejection. */
69970
70121
  var lastRejectedKey = "";
70122
+ /**
70123
+ * Serials last applied from host `assignedCameras`.
70124
+ * Empty + never synced ⇒ do not strip cameras that came from floorData
70125
+ * (host may load a saved floor without passing assignedCameras).
70126
+ */
70127
+ var lastSyncedSerials = /* @__PURE__ */ new Set();
70128
+ /** Call when host replaces floorData so load doesn't fight a stale sync set. */
70129
+ function resetAssignedCamerasSyncState() {
70130
+ lastSyncedSerials = /* @__PURE__ */ new Set();
70131
+ lastRejectedKey = "";
70132
+ }
69971
70133
  function getCameraFurniture(floor) {
69972
70134
  return floor.furniture.filter((f) => f.catalogId === "camera");
69973
70135
  }
@@ -70053,9 +70215,11 @@ function syncAssignedCameras(assigned) {
70053
70215
  });
70054
70216
  }
70055
70217
  if (toAdd.length > 0 && !hasFloorLayout) rejectNoFloor(toAdd);
70218
+ const newSerials = new Set(normalized.map((c) => String(c.serial).trim()));
70219
+ const hostDrivesAssignment = newSerials.size > 0 || lastSyncedSerials.size > 0;
70056
70220
  setAssignedCamerasSyncing(true);
70057
70221
  try {
70058
- for (const item of existing) {
70222
+ if (hostDrivesAssignment) for (const item of existing) {
70059
70223
  if (!item.serial) continue;
70060
70224
  if (!assignedBySerial.has(item.serial)) removeFurniture(item.id);
70061
70225
  }
@@ -70069,10 +70233,14 @@ function syncAssignedCameras(assigned) {
70069
70233
  nextProps.serial = item.serial;
70070
70234
  if (!propsEqual(item.props, nextProps) || item.name !== nextProps.name) applyCameraProps(item.id, assignedCam);
70071
70235
  }
70072
- if (!hasFloorLayout) return;
70236
+ if (!hasFloorLayout) {
70237
+ if (hostDrivesAssignment || newSerials.size > 0) lastSyncedSerials = newSerials;
70238
+ return;
70239
+ }
70073
70240
  const center = getFloorCenter(floorAfterRemove);
70074
70241
  if (!center) {
70075
70242
  rejectNoFloor(toAdd);
70243
+ if (hostDrivesAssignment || newSerials.size > 0) lastSyncedSerials = newSerials;
70076
70244
  return;
70077
70245
  }
70078
70246
  lastRejectedKey = "";
@@ -70083,6 +70251,7 @@ function syncAssignedCameras(assigned) {
70083
70251
  applyCameraProps(addFurniture("camera", findOpenCameraPosition(workingFloor, center)), cam);
70084
70252
  workingFloor = get$1(activeFloor);
70085
70253
  }
70254
+ lastSyncedSerials = newSerials;
70086
70255
  } finally {
70087
70256
  setAssignedCamerasSyncing(false);
70088
70257
  }
@@ -70134,6 +70303,7 @@ function App($$anchor, $$props) {
70134
70303
  set(mode, m, true);
70135
70304
  });
70136
70305
  user_effect(() => {
70306
+ resetAssignedCamerasSyncState();
70137
70307
  currentProject.set(normalizeFloorData(get(floorData)));
70138
70308
  });
70139
70309
  user_effect(() => {
@@ -70176,20 +70346,29 @@ function App($$anchor, $$props) {
70176
70346
  var div = first_child(fragment);
70177
70347
  var node = child(div);
70178
70348
  var consequent = ($$anchor) => {
70179
- TopBar($$anchor, {
70180
- get viewOnly() {
70181
- return get(config).viewOnly;
70182
- },
70183
- get onDispatch() {
70184
- return $$props.onDispatch;
70185
- },
70186
- get showBuildSidebar() {
70187
- return get(showBuildSidebar);
70188
- },
70189
- set showBuildSidebar($$value) {
70190
- set(showBuildSidebar, $$value, true);
70191
- }
70192
- });
70349
+ {
70350
+ let $0 = /* @__PURE__ */ user_derived(() => {
70351
+ var _$$get$headerToolsMod;
70352
+ return (_$$get$headerToolsMod = get(config).headerToolsMode) !== null && _$$get$headerToolsMod !== void 0 ? _$$get$headerToolsMod : "full";
70353
+ });
70354
+ TopBar($$anchor, {
70355
+ get viewOnly() {
70356
+ return get(config).viewOnly;
70357
+ },
70358
+ get headerToolsMode() {
70359
+ return get($0);
70360
+ },
70361
+ get onDispatch() {
70362
+ return $$props.onDispatch;
70363
+ },
70364
+ get showBuildSidebar() {
70365
+ return get(showBuildSidebar);
70366
+ },
70367
+ set showBuildSidebar($$value) {
70368
+ set(showBuildSidebar, $$value, true);
70369
+ }
70370
+ });
70371
+ }
70193
70372
  };
70194
70373
  if_block(node, ($$render) => {
70195
70374
  if (!get(config).viewOnly) $$render(consequent);