floor-editor-ts 1.3.5 → 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.
@@ -6578,10 +6578,30 @@ function updateWindow(id, updates) {
6578
6578
  });
6579
6579
  }
6580
6580
  function updateFurniture(id, updates) {
6581
+ const p = get$1(currentProject);
6582
+ const floor = p === null || p === void 0 ? void 0 : p.floors.find((f) => f.id === p.activeFloorId);
6583
+ const before = floor === null || floor === void 0 ? void 0 : floor.furniture.find((fi) => fi.id === id);
6584
+ const beforeSerial = (before === null || before === void 0 ? void 0 : before.catalogId) === "camera" ? before.serial || void 0 : void 0;
6585
+ const beforeProps = (before === null || before === void 0 ? void 0 : before.catalogId) === "camera" ? cameraItemToCallbackProps(before) : null;
6581
6586
  mutate((f) => {
6582
6587
  const fi = f.furniture.find((fi) => fi.id === id);
6583
6588
  if (fi) Object.assign(fi, updates);
6584
6589
  });
6590
+ if ((before === null || before === void 0 ? void 0 : before.catalogId) !== "camera" || isAssignedCamerasSyncing()) return;
6591
+ const afterProject = get$1(currentProject);
6592
+ const afterFloor = afterProject === null || afterProject === void 0 ? void 0 : afterProject.floors.find((f) => f.id === afterProject.activeFloorId);
6593
+ const after = afterFloor === null || afterFloor === void 0 ? void 0 : afterFloor.furniture.find((fi) => fi.id === id);
6594
+ const afterSerial = (after === null || after === void 0 ? void 0 : after.serial) || void 0;
6595
+ if (beforeSerial === afterSerial) return;
6596
+ const added = [];
6597
+ const removed = [];
6598
+ if (beforeSerial && beforeProps) removed.push(beforeProps);
6599
+ const afterProps = after ? cameraItemToCallbackProps(after) : null;
6600
+ if (afterSerial && afterProps) added.push(afterProps);
6601
+ emitCamerasChanged({
6602
+ added,
6603
+ removed
6604
+ });
6585
6605
  }
6586
6606
  function updateRoom(id, updates) {
6587
6607
  mutate((f) => {
@@ -7100,7 +7120,8 @@ var DEFAULT_CONFIG = {
7100
7120
  cameraThumbnail: "",
7101
7121
  cameraThumbnailOpacity: .65,
7102
7122
  canvasBackground: "#f8f9fa",
7103
- floorPlateBackground: "#f8f9fa"
7123
+ floorPlateBackground: "#f8f9fa",
7124
+ headerToolsMode: "full"
7104
7125
  };
7105
7126
  function normalizeConfig(value) {
7106
7127
  if (!value || typeof value !== "object") return { ...DEFAULT_CONFIG };
@@ -7118,6 +7139,7 @@ function normalizeConfig(value) {
7118
7139
  const opacity = typeof value.cameraThumbnailOpacity === "number" && value.cameraThumbnailOpacity >= 0 && value.cameraThumbnailOpacity <= 1 ? value.cameraThumbnailOpacity : DEFAULT_CONFIG.cameraThumbnailOpacity;
7119
7140
  const canvasBackground = normalizeColor$1(value.canvasBackground, DEFAULT_CONFIG.canvasBackground);
7120
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;
7121
7143
  return {
7122
7144
  ...DEFAULT_CONFIG,
7123
7145
  ...value,
@@ -7126,7 +7148,8 @@ function normalizeConfig(value) {
7126
7148
  cameraThumbnail: image,
7127
7149
  cameraThumbnailOpacity: opacity,
7128
7150
  canvasBackground,
7129
- floorPlateBackground
7151
+ floorPlateBackground,
7152
+ headerToolsMode
7130
7153
  };
7131
7154
  }
7132
7155
  function normalizeColor$1(value, fallback) {
@@ -7135,16 +7158,31 @@ function normalizeColor$1(value, fallback) {
7135
7158
  if (/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(trimmed)) return trimmed;
7136
7159
  return fallback;
7137
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
+ */
7138
7168
  function normalizeFloorData(data) {
7139
7169
  if (!data || typeof data !== "object") return createDefaultProject();
7140
- if (!Array.isArray(data.floors) || data.floors.length === 0) return createDefaultProject();
7141
- 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 {
7142
7181
  ...createDefaultProject(),
7143
- ...data,
7144
- floors: data.floors,
7145
- activeFloorId: data.floors[0].id
7182
+ ...rest,
7183
+ floors,
7184
+ activeFloorId
7146
7185
  };
7147
- return data;
7148
7186
  }
7149
7187
  var RUNTIME_FURNITURE_FIELDS = [
7150
7188
  "showHeatmap",
@@ -56244,11 +56282,11 @@ var root_7$6 = /* @__PURE__ */ from_html(`<div class="flex items-center justify-
56244
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
56245
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
56246
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);
56247
- 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>`);
56248
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>`);
56249
- 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>`);
56250
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>`);
56251
- 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>`);
56252
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>`);
56253
56291
  function SettingsDialog($$anchor, $$props) {
56254
56292
  push($$props, true);
@@ -56499,7 +56537,7 @@ function SettingsDialog($$anchor, $$props) {
56499
56537
  let value = () => get($$array)[0];
56500
56538
  let icon = () => get($$array)[1];
56501
56539
  let label = () => get($$array)[2];
56502
- var button_9 = root_9$2();
56540
+ var button_9 = root_9$3();
56503
56541
  var span = child(button_9);
56504
56542
  var text = child(span, true);
56505
56543
  reset(span);
@@ -56521,7 +56559,7 @@ function SettingsDialog($$anchor, $$props) {
56521
56559
  append($$anchor, div_15);
56522
56560
  };
56523
56561
  var consequent_9 = ($$anchor) => {
56524
- var div_18 = root_10$2();
56562
+ var div_18 = root_10$3();
56525
56563
  var div_19 = child(div_18);
56526
56564
  var div_20 = sibling(child(div_19), 4);
56527
56565
  var div_21 = child(div_20);
@@ -56538,7 +56576,7 @@ function SettingsDialog($$anchor, $$props) {
56538
56576
  reset(button_11);
56539
56577
  var node_6 = sibling(button_11, 2);
56540
56578
  var consequent_7 = ($$anchor) => {
56541
- var button_12 = root_11$2();
56579
+ var button_12 = root_11$3();
56542
56580
  delegated("click", button_12, clearGeminiKey);
56543
56581
  append($$anchor, button_12);
56544
56582
  };
@@ -57109,14 +57147,19 @@ init_onboarding_svelte();
57109
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"/>`);
57110
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>`);
57111
57149
  var root_4$8 = /* @__PURE__ */ from_html(`<button> </button>`);
57112
- 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>`);
57113
- 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>`);
57114
- 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>`);
57115
- 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>`);
57116
- 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>`);
57117
- 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>`);
57118
- 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>`);
57119
- 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>`);
57120
57163
  var root$5 = /* @__PURE__ */ from_html(`<!> <!> <!> <!>`, 1);
57121
57164
  function TopBar($$anchor, $$props) {
57122
57165
  push($$props, true);
@@ -57125,10 +57168,17 @@ function TopBar($$anchor, $$props) {
57125
57168
  const $canvasZoom = () => store_get(canvasZoom, "$canvasZoom", $$stores);
57126
57169
  const $saveState = () => store_get(saveState, "$saveState", $$stores);
57127
57170
  const [$$stores, $$cleanup] = setup_stores();
57128
- 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");
57129
57174
  let settingsOpen = /* @__PURE__ */ state(false);
57130
57175
  let areaOpen = /* @__PURE__ */ state(false);
57131
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));
57132
57182
  let projectName = /* @__PURE__ */ state("");
57133
57183
  let mode = /* @__PURE__ */ state("2d");
57134
57184
  let floors = /* @__PURE__ */ state(proxy([]));
@@ -57247,15 +57297,27 @@ function TopBar($$anchor, $$props) {
57247
57297
  currentProject.set(createDefaultProject());
57248
57298
  set(exportOpen, false);
57249
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
+ }
57250
57310
  onMount(() => {
57251
57311
  initAutoSave($$props.onDispatch);
57252
57312
  initVersionHistory();
57253
57313
  const interval = setInterval(updateLastSavedText, 15e3);
57254
57314
  function handleClickOutside(e) {
57255
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);
57256
57317
  }
57257
57318
  function handleKeydown(e) {
57258
57319
  if (get(exportOpen)) set(exportOpen, false);
57320
+ if (e.key === "Escape" && get(toolsMenuOpen)) set(toolsMenuOpen, false);
57259
57321
  if (e.key === "Escape" && get(versionHistoryOpen)) set(versionHistoryOpen, false);
57260
57322
  if (e.key === "Escape" && get(areaOpen)) set(areaOpen, false);
57261
57323
  }
@@ -57267,6 +57329,28 @@ function TopBar($$anchor, $$props) {
57267
57329
  clearInterval(interval);
57268
57330
  };
57269
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
+ });
57270
57354
  function onImportJSON() {
57271
57355
  const input = document.createElement("input");
57272
57356
  input.type = "file";
@@ -57306,7 +57390,7 @@ function TopBar($$anchor, $$props) {
57306
57390
  }
57307
57391
  var fragment = root$5();
57308
57392
  var node = first_child(fragment);
57309
- var consequent_7 = ($$anchor) => {
57393
+ var consequent_11 = ($$anchor) => {
57310
57394
  var div = root_1$10();
57311
57395
  var button = child(div);
57312
57396
  var node_1 = sibling(button, 2);
@@ -57352,10 +57436,22 @@ function TopBar($$anchor, $$props) {
57352
57436
  reset(div_1);
57353
57437
  var button_4 = sibling(div_1, 4);
57354
57438
  var button_5 = sibling(button_4, 2);
57355
- var button_6 = sibling(button_5, 4);
57356
- var node_3 = sibling(button_6, 2);
57439
+ var node_3 = sibling(button_5, 4);
57357
57440
  var consequent_1 = ($$anchor) => {
57358
- 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();
57359
57455
  var button_7 = child(div_2);
57360
57456
  var button_8 = sibling(button_7, 2);
57361
57457
  reset(div_2);
@@ -57367,13 +57463,25 @@ function TopBar($$anchor, $$props) {
57367
57463
  delegated("click", button_8, () => panMode.set(true));
57368
57464
  append($$anchor, div_2);
57369
57465
  };
57370
- if_block(node_3, ($$render) => {
57371
- if (get(mode) === "2d") $$render(consequent_1);
57466
+ if_block(node_4, ($$render) => {
57467
+ if (get(mode) === "2d") $$render(consequent_2);
57372
57468
  });
57373
- var button_9 = sibling(node_3, 2);
57374
- var node_4 = sibling(button_9, 4);
57375
- var consequent_2 = ($$anchor) => {
57376
- 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();
57377
57485
  var button_10 = child(div_3);
57378
57486
  var button_11 = sibling(button_10, 2);
57379
57487
  var text_4 = child(button_11);
@@ -57386,77 +57494,146 @@ function TopBar($$anchor, $$props) {
57386
57494
  delegated("click", button_12, () => canvasZoom.update((z) => Math.min(10, z * 1.25)));
57387
57495
  append($$anchor, div_3);
57388
57496
  };
57389
- if_block(node_4, ($$render) => {
57390
- if (get(mode) === "2d") $$render(consequent_2);
57497
+ if_block(node_6, ($$render) => {
57498
+ if (get(mode) === "2d") $$render(consequent_4);
57391
57499
  });
57392
- var button_13 = sibling(node_4, 2);
57393
- var button_14 = sibling(button_13, 2);
57394
- var button_15 = sibling(button_14, 2);
57395
- var div_4 = sibling(button_15, 4);
57396
- var button_16 = child(div_4);
57397
- var node_5 = sibling(button_16, 2);
57398
- var consequent_4 = ($$anchor) => {
57399
- var div_5 = root_7$5();
57400
- var button_17 = child(div_5);
57401
- var button_18 = sibling(button_17, 4);
57402
- 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);
57403
57560
  var button_20 = sibling(button_19, 2);
57404
57561
  var button_21 = sibling(button_20, 2);
57405
- var button_22 = sibling(button_21, 2);
57406
- var button_23 = sibling(button_22, 2);
57407
- var button_24 = sibling(button_23, 2);
57408
- var node_6 = sibling(button_24, 4);
57409
- var consequent_3 = ($$anchor) => {
57410
- var button_25 = root_8$2();
57411
- delegated("click", button_25, onImportJSON);
57412
- 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);
57413
57589
  };
57414
- if_block(node_6, ($$render) => {
57415
- if (!$$props.viewOnly) $$render(consequent_3);
57590
+ if_block(node_10, ($$render) => {
57591
+ if (!$$props.viewOnly) $$render(consequent_7);
57416
57592
  });
57417
- var button_26 = sibling(node_6, 2);
57418
- reset(div_5);
57419
- delegated("click", button_17, () => {
57593
+ var button_32 = sibling(node_10, 2);
57594
+ reset(div_7);
57595
+ delegated("click", button_23, () => {
57420
57596
  set(exportOpen, false);
57421
57597
  window.dispatchEvent(new KeyboardEvent("keydown", {
57422
57598
  key: "p",
57423
57599
  ctrlKey: true
57424
57600
  }));
57425
57601
  });
57426
- delegated("click", button_18, onExport2DPNG);
57427
- delegated("click", button_19, onExport3DPNG);
57428
- delegated("click", button_20, onExportSVG);
57429
- delegated("click", button_21, onExportDXF);
57430
- delegated("click", button_22, onExportDWG);
57431
- delegated("click", button_23, onExportPDF);
57432
- delegated("click", button_24, onExportJSON);
57433
- delegated("click", button_26, newProject);
57434
- 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);
57435
57611
  };
57436
- if_block(node_5, ($$render) => {
57437
- if (get(exportOpen)) $$render(consequent_4);
57612
+ if_block(node_9, ($$render) => {
57613
+ if (get(exportOpen)) $$render(consequent_8);
57438
57614
  });
57439
- reset(div_4);
57440
- bind_this(div_4, ($$value) => exportRef = $$value, () => exportRef);
57441
- var span_1 = sibling(div_4, 2);
57442
- var node_7 = child(span_1);
57443
- 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) => {
57444
57620
  append($$anchor, text$1("Saving…"));
57445
57621
  };
57446
- var consequent_6 = ($$anchor) => {
57622
+ var consequent_10 = ($$anchor) => {
57447
57623
  append($$anchor, text$1("Saved ✓"));
57448
57624
  };
57449
- var alternate_1 = ($$anchor) => {
57625
+ var alternate_2 = ($$anchor) => {
57450
57626
  append($$anchor, text$1("Unsaved •"));
57451
57627
  };
57452
- if_block(node_7, ($$render) => {
57453
- if ($saveState() === "saving") $$render(consequent_5);
57454
- else if ($saveState() === "saved") $$render(consequent_6, 1);
57455
- 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);
57456
57632
  });
57457
- reset(span_1);
57458
- var button_27 = sibling(span_1, 2);
57633
+ reset(span_3);
57634
+ var button_33 = sibling(span_3, 2);
57459
57635
  reset(div);
57636
+ bind_this(div, ($$value) => set(headerEl, $$value), () => get(headerEl));
57460
57637
  template_effect(() => {
57461
57638
  var _$$get$length;
57462
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" : ""}`);
@@ -57464,12 +57641,8 @@ function TopBar($$anchor, $$props) {
57464
57641
  set_attribute(button, "aria-label", showBuildSidebar() ? "Hide sidebar" : "Show sidebar");
57465
57642
  set_attribute(button, "aria-pressed", showBuildSidebar());
57466
57643
  set_text(text_3, `${(_$$get$length = get(floors).length) !== null && _$$get$length !== void 0 ? _$$get$length : ""}F`);
57467
- 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"}`);
57468
- set_attribute(button_6, "title", `Snap to Grid (${get(snapOn) ? "On" : "Off"})`);
57469
- 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"}`);
57470
- set_attribute(button_9, "title", `Toggle Furniture (${$showFurnitureStore() ? "Visible" : "Hidden"})`);
57471
- 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"}`);
57472
- 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");
57473
57646
  });
57474
57647
  delegated("click", button, () => showBuildSidebar(!showBuildSidebar()));
57475
57648
  delegated("click", button_3, onAddFloor);
@@ -57479,80 +57652,69 @@ function TopBar($$anchor, $$props) {
57479
57652
  delegated("click", button_5, function(...$$args) {
57480
57653
  redo === null || redo === void 0 || redo.apply(this, $$args);
57481
57654
  });
57482
- delegated("click", button_6, () => {
57483
- snapEnabled.update((v) => !v);
57484
- set(snapOn, !get(snapOn));
57485
- });
57486
- delegated("click", button_9, () => layerVisibility.update((v) => ({
57487
- ...v,
57488
- furniture: !v.furniture
57489
- })));
57490
- delegated("click", button_13, () => set(versionHistoryOpen, true));
57491
- delegated("click", button_14, () => set(areaOpen, true));
57492
- delegated("click", button_15, () => set(settingsOpen, true));
57493
- delegated("click", button_16, () => {
57655
+ delegated("click", button_22, () => {
57494
57656
  set(exportOpen, !get(exportOpen));
57495
57657
  if (get(exportOpen)) triggerTip("first-export", 300, 60);
57496
57658
  });
57497
- delegated("click", button_27, save);
57659
+ delegated("click", button_33, save);
57498
57660
  append($$anchor, div);
57499
57661
  };
57500
- var alternate_2 = ($$anchor) => {
57501
- var div_6 = root_12$2();
57502
- var span_2 = child(div_6);
57503
- var text_8 = child(span_2, true);
57504
- reset(span_2);
57505
- var div_7 = sibling(span_2, 2);
57506
- var button_28 = child(div_7);
57507
- var node_8 = sibling(button_28, 2);
57508
- var consequent_8 = ($$anchor) => {
57509
- var div_8 = root_13$2();
57510
- var button_29 = child(div_8);
57511
- var button_30 = sibling(button_29, 4);
57512
- var button_31 = sibling(button_30, 2);
57513
- var button_32 = sibling(button_31, 2);
57514
- var button_33 = sibling(button_32, 2);
57515
- var button_34 = sibling(button_33, 2);
57516
- var button_35 = sibling(button_34, 2);
57517
- var button_36 = sibling(button_35, 2);
57518
- var button_37 = sibling(button_36, 4);
57519
- reset(div_8);
57520
- 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, () => {
57521
57683
  set(exportOpen, false);
57522
57684
  window.dispatchEvent(new KeyboardEvent("keydown", {
57523
57685
  key: "p",
57524
57686
  ctrlKey: true
57525
57687
  }));
57526
57688
  });
57527
- delegated("click", button_30, onExport2DPNG);
57528
- delegated("click", button_31, onExport3DPNG);
57529
- delegated("click", button_32, onExportSVG);
57530
- delegated("click", button_33, onExportDXF);
57531
- delegated("click", button_34, onExportDWG);
57532
- delegated("click", button_35, onExportPDF);
57533
- delegated("click", button_36, onExportJSON);
57534
- delegated("click", button_37, onImportJSON);
57535
- 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);
57536
57698
  };
57537
- if_block(node_8, ($$render) => {
57538
- if (get(exportOpen)) $$render(consequent_8);
57699
+ if_block(node_12, ($$render) => {
57700
+ if (get(exportOpen)) $$render(consequent_12);
57539
57701
  });
57540
- reset(div_7);
57541
- bind_this(div_7, ($$value) => exportRef = $$value, () => exportRef);
57542
- reset(div_6);
57543
- template_effect(() => set_text(text_8, get(projectName)));
57544
- 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, () => {
57545
57707
  set(exportOpen, !get(exportOpen));
57546
57708
  if (get(exportOpen)) triggerTip("first-export", 300, 60);
57547
57709
  });
57548
- append($$anchor, div_6);
57710
+ append($$anchor, div_8);
57549
57711
  };
57550
57712
  if_block(node, ($$render) => {
57551
- if (!$$props.viewOnly) $$render(consequent_7);
57552
- else $$render(alternate_2, -1);
57713
+ if (!$$props.viewOnly) $$render(consequent_11);
57714
+ else $$render(alternate_3, -1);
57553
57715
  });
57554
- var node_9 = sibling(node, 2);
57555
- SettingsDialog(node_9, {
57716
+ var node_13 = sibling(node, 2);
57717
+ SettingsDialog(node_13, {
57556
57718
  get open() {
57557
57719
  return get(settingsOpen);
57558
57720
  },
@@ -57560,8 +57722,8 @@ function TopBar($$anchor, $$props) {
57560
57722
  set(settingsOpen, $$value, true);
57561
57723
  }
57562
57724
  });
57563
- var node_10 = sibling(node_9, 2);
57564
- VersionHistoryPanel(node_10, {
57725
+ var node_14 = sibling(node_13, 2);
57726
+ VersionHistoryPanel(node_14, {
57565
57727
  get open() {
57566
57728
  return get(versionHistoryOpen);
57567
57729
  },
@@ -57569,28 +57731,28 @@ function TopBar($$anchor, $$props) {
57569
57731
  set(versionHistoryOpen, $$value, true);
57570
57732
  }
57571
57733
  });
57572
- var node_11 = sibling(node_10, 2);
57573
- var consequent_9 = ($$anchor) => {
57574
- var div_9 = root_14$2();
57575
- var div_10 = child(div_9);
57576
- var div_11 = child(div_10);
57577
- var button_38 = sibling(child(div_11), 2);
57578
- reset(div_11);
57579
- var div_12 = sibling(div_11, 2);
57580
- 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);
57581
57744
  reset(div_12);
57582
- reset(div_10);
57583
- reset(div_9);
57584
- delegated("click", div_9, () => set(areaOpen, false));
57585
- delegated("keydown", div_9, (e) => {
57745
+ reset(div_11);
57746
+ delegated("click", div_11, () => set(areaOpen, false));
57747
+ delegated("keydown", div_11, (e) => {
57586
57748
  if (e.key === "Escape") set(areaOpen, false);
57587
57749
  });
57588
- delegated("click", div_10, (e) => e.stopPropagation());
57589
- delegated("click", button_38, () => set(areaOpen, false));
57590
- 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);
57591
57753
  };
57592
- if_block(node_11, ($$render) => {
57593
- if (get(areaOpen)) $$render(consequent_9);
57754
+ if_block(node_15, ($$render) => {
57755
+ if (get(areaOpen)) $$render(consequent_13);
57594
57756
  });
57595
57757
  append($$anchor, fragment);
57596
57758
  pop();
@@ -59512,7 +59674,7 @@ var root_26 = /* @__PURE__ */ from_html(`<label class="block"><span class="text-
59512
59674
  var root_24$1 = /* @__PURE__ */ from_html(`<h3 class="text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2"><span class="w-6 h-6 bg-gray-200 rounded flex items-center justify-center text-xs">🏛️</span> Column Properties</h3> <div class="space-y-3"><label class="block"><span class="text-xs text-gray-500">Shape</span> <div class="flex gap-2"><button>⭕ Round</button> <button>⬜ Square</button></div></label> <label class="block"><span class="text-xs text-gray-500"> </span> <input type="number" min="10" max="200" class="w-full px-2 py-1 border border-gray-200 rounded text-sm"/></label> <label class="block"><span class="text-xs text-gray-500"> </span> <input type="number" min="50" max="1000" class="w-full px-2 py-1 border border-gray-200 rounded text-sm"/></label> <div><span class="text-xs text-gray-500 mb-1.5 block">Color</span> <div class="grid grid-cols-5 gap-1.5 mb-2"></div> <div class="flex items-center gap-2"><span class="text-xs text-gray-500">Custom:</span> <input type="color" class="w-8 h-6 rounded border border-gray-200 cursor-pointer"/></div></div> <!></div>`, 1);
59513
59675
  var root_27 = /* @__PURE__ */ from_html(`<div class="space-y-3"><h3 class="text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2"><span class="w-6 h-6 bg-emerald-100 rounded flex items-center justify-center text-xs">🏷️</span> Text Annotation</h3> <label class="block"><span class="text-xs text-gray-500">Text</span> <input type="text" class="w-full px-2 py-1 border border-gray-200 rounded text-sm"/></label> <label class="block"><span class="text-xs text-gray-500">Font Size</span> <input type="number" min="8" max="72" class="w-full px-2 py-1 border border-gray-200 rounded text-sm"/></label> <label class="block"><span class="text-xs text-gray-500">Color</span> <div class="flex items-center gap-2"><input type="color" class="w-8 h-6 rounded border border-gray-200 cursor-pointer"/> <span class="text-xs text-gray-400"> </span></div></label> <label class="block"><span class="text-xs text-gray-500">Rotation (°)</span> <input type="number" class="w-full px-2 py-1 border border-gray-200 rounded text-sm"/></label> <label class="block"><span class="text-xs text-gray-500">X</span> <input type="number" class="w-full px-2 py-1 border border-gray-200 rounded text-sm"/></label> <label class="block"><span class="text-xs text-gray-500">Y</span> <input type="number" class="w-full px-2 py-1 border border-gray-200 rounded text-sm"/></label></div>`);
59514
59676
  var root_28 = /* @__PURE__ */ from_html(`<div class="mt-4 pt-3 border-t border-gray-200"><h3 class="text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2"><span class="w-6 h-6 bg-blue-100 rounded flex items-center justify-center text-xs">🖼️</span> Background Image</h3> <div class="space-y-3"><label class="block"><span class="text-xs text-gray-500">Opacity</span> <input type="range" min="0.05" max="1" step="0.05" class="w-full"/></label> <label class="block"><span class="text-xs text-gray-500">Scale</span> <input type="range" min="0.1" max="5" step="0.05" class="w-full"/></label> <label class="block"><span class="text-xs text-gray-500">Rotation</span> <input type="number" class="w-full px-2 py-1 border border-gray-200 rounded text-sm"/></label> <div class="flex gap-2"><button> </button> <button class="flex-1 px-2 py-1.5 border rounded text-sm border-gray-200 hover:bg-gray-50">📏 Set Scale</button></div> <button class="w-full px-2 py-1.5 border border-red-300 rounded text-sm text-red-600 hover:bg-red-50">Remove Image</button></div></div>`);
59515
- var root$3 = /* @__PURE__ */ from_html(`<div data-properties-panel="" style="top: 48px; bottom: 36px;"><!> <!></div>`);
59677
+ var root$3 = /* @__PURE__ */ from_html(`<div data-properties-panel="" style="top: 0; bottom: 0;"><!> <!></div>`);
59516
59678
  function PropertiesPanel($$anchor, $$props) {
59517
59679
  push($$props, true);
59518
59680
  const $projectSettings = () => store_get(projectSettings, "$projectSettings", $$stores);
@@ -61116,7 +61278,7 @@ function PropertiesPanel($$anchor, $$props) {
61116
61278
  if (get(hasBgImage) && ((_$$get19 = get(floor)) === null || _$$get19 === void 0 ? void 0 : _$$get19.backgroundImage)) $$render(consequent_14);
61117
61279
  });
61118
61280
  reset(div);
61119
- template_effect(() => classes = set_class(div, 1, `${is3D() ? "w-80" : "w-64"} shrink-0 bg-white border-l border-gray-200 flex flex-col overflow-y-auto p-3 fixed right-0 z-40 shadow-lg`, null, classes, { hidden: !get(hasSelection) }));
61281
+ template_effect(() => classes = set_class(div, 1, `${is3D() ? "w-80" : "w-64"} shrink-0 bg-white border-l border-gray-200 flex flex-col overflow-y-auto p-3 absolute right-0 z-40 shadow-lg`, null, classes, { hidden: !get(hasSelection) }));
61120
61282
  delegated("keydown", div, stopShortcutBubble);
61121
61283
  event("keydown", div, stopShortcutBubble, true);
61122
61284
  append($$anchor, div);
@@ -66736,13 +66898,19 @@ function FloorPlanCanvas($$anchor, $$props) {
66736
66898
  markDirty();
66737
66899
  });
66738
66900
  const unsubZoom = canvasZoom.subscribe((v) => {
66901
+ if (get(zoom) === v) return;
66739
66902
  set(zoom, v, true);
66903
+ markDirty();
66740
66904
  });
66741
66905
  const unsubCamX = canvasCamX.subscribe((v) => {
66906
+ if (get(camX) === v) return;
66742
66907
  set(camX, v, true);
66908
+ markDirty();
66743
66909
  });
66744
66910
  const unsubCamY = canvasCamY.subscribe((v) => {
66911
+ if (get(camY) === v) return;
66745
66912
  set(camY, v, true);
66913
+ markDirty();
66746
66914
  });
66747
66915
  function handlePaste(e) {
66748
66916
  if (!e.clipboardData) return;
@@ -68941,12 +69109,15 @@ function FloorPlanCanvas($$anchor, $$props) {
68941
69109
  set(camX, worldCX - (get(width) / 2 - get(width) / 2) / newZoom);
68942
69110
  set(camY, worldCY - (get(height) / 2 - get(height) / 2) / newZoom);
68943
69111
  set(zoom, newZoom, true);
69112
+ markDirty();
68944
69113
  });
68945
69114
  delegated("click", button_12, () => {
68946
69115
  set(zoom, 1);
69116
+ markDirty();
68947
69117
  });
68948
69118
  delegated("click", button_13, () => {
68949
69119
  set(zoom, Math.min(10, get(zoom) * 1.25), true);
69120
+ markDirty();
68950
69121
  });
68951
69122
  delegated("click", button_14, () => zoomToFit());
68952
69123
  append($$anchor, fragment);
@@ -69948,6 +70119,17 @@ var CAMERA_SPACING = 40;
69948
70119
  var CAMERA_HIT = 28;
69949
70120
  /** Avoid spamming host with the same rejection. */
69950
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
+ }
69951
70133
  function getCameraFurniture(floor) {
69952
70134
  return floor.furniture.filter((f) => f.catalogId === "camera");
69953
70135
  }
@@ -70033,9 +70215,11 @@ function syncAssignedCameras(assigned) {
70033
70215
  });
70034
70216
  }
70035
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;
70036
70220
  setAssignedCamerasSyncing(true);
70037
70221
  try {
70038
- for (const item of existing) {
70222
+ if (hostDrivesAssignment) for (const item of existing) {
70039
70223
  if (!item.serial) continue;
70040
70224
  if (!assignedBySerial.has(item.serial)) removeFurniture(item.id);
70041
70225
  }
@@ -70049,10 +70233,14 @@ function syncAssignedCameras(assigned) {
70049
70233
  nextProps.serial = item.serial;
70050
70234
  if (!propsEqual(item.props, nextProps) || item.name !== nextProps.name) applyCameraProps(item.id, assignedCam);
70051
70235
  }
70052
- if (!hasFloorLayout) return;
70236
+ if (!hasFloorLayout) {
70237
+ if (hostDrivesAssignment || newSerials.size > 0) lastSyncedSerials = newSerials;
70238
+ return;
70239
+ }
70053
70240
  const center = getFloorCenter(floorAfterRemove);
70054
70241
  if (!center) {
70055
70242
  rejectNoFloor(toAdd);
70243
+ if (hostDrivesAssignment || newSerials.size > 0) lastSyncedSerials = newSerials;
70056
70244
  return;
70057
70245
  }
70058
70246
  lastRejectedKey = "";
@@ -70063,6 +70251,7 @@ function syncAssignedCameras(assigned) {
70063
70251
  applyCameraProps(addFurniture("camera", findOpenCameraPosition(workingFloor, center)), cam);
70064
70252
  workingFloor = get$1(activeFloor);
70065
70253
  }
70254
+ lastSyncedSerials = newSerials;
70066
70255
  } finally {
70067
70256
  setAssignedCamerasSyncing(false);
70068
70257
  }
@@ -70074,7 +70263,7 @@ init_index_client();
70074
70263
  var root_2 = /* @__PURE__ */ from_html(`<div class="shrink-0 h-full overflow-hidden transition-[width] duration-300 ease-in-out"><div><!></div></div>`);
70075
70264
  var root_3 = /* @__PURE__ */ from_html(`<!> <!>`, 1);
70076
70265
  var root_7 = /* @__PURE__ */ from_html(`<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50" role="dialog" tabindex="-1" aria-label="Keyboard Shortcuts"><div class="bg-white rounded-2xl shadow-2xl max-w-2xl w-full mx-4 max-h-[85vh] flex flex-col" role="document"><div class="flex items-center justify-between px-6 pt-5 pb-3 border-b border-gray-100"><div class="flex items-center gap-2"><svg class="w-5 h-5 text-slate-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707"></path></svg> <h2 class="text-lg font-bold text-slate-800">Keyboard Shortcuts</h2></div> <div class="flex items-center gap-2"><button class="text-xs px-3 py-1.5 rounded-lg bg-slate-100 hover:bg-slate-200 text-slate-600 hover:text-slate-800 transition-colors flex items-center gap-1.5" aria-label="Copy all shortcuts"><svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg> Copy All</button> <button class="text-gray-400 hover:text-gray-600 text-xl leading-none" aria-label="Close shortcuts">✕</button></div></div> <div class="overflow-y-auto px-6 py-4"><div class="grid grid-cols-2 gap-x-8 gap-y-0 text-sm"><div><div class="flex items-center gap-2 mb-2"><span class="text-xs font-bold uppercase tracking-wider text-indigo-500">Tools</span> <div class="flex-1 h-px bg-indigo-100"></div></div> <div class="space-y-1.5 mb-5"><div class="flex justify-between"><span class="text-gray-600">Select tool</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">V</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Wall tool</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">W</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Door tool</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">D</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Pan mode</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">H</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Measure tool</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">M</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Annotate tool</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">N</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Text tool</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">T</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Toggle snap</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">S</kbd></div></div> <div class="flex items-center gap-2 mb-2"><span class="text-xs font-bold uppercase tracking-wider text-amber-500">Edit</span> <div class="flex-1 h-px bg-amber-100"></div></div> <div class="space-y-1.5 mb-5"><div class="flex justify-between"><span class="text-gray-600">Undo</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+Z</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Redo</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+Y</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Copy</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+C</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Paste</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+V</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Select all</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+A</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Deselect all</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+D</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Save project</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+S</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Cancel / Deselect</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Esc</kbd></div></div></div> <div><div class="flex items-center gap-2 mb-2"><span class="text-xs font-bold uppercase tracking-wider text-emerald-500">Elements</span> <div class="flex-1 h-px bg-emerald-100"></div></div> <div class="space-y-1.5 mb-5"><div class="flex justify-between"><span class="text-gray-600">Rotate element</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">R</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Delete selected</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Del</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Lock / Unlock</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+L</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Group selection</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+G</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Ungroup</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Ctrl+⇧+G</kbd></div></div> <div class="flex items-center gap-2 mb-2"><span class="text-xs font-bold uppercase tracking-wider text-blue-500">View</span> <div class="flex-1 h-px bg-blue-100"></div></div> <div class="space-y-1.5 mb-5"><div class="flex justify-between"><span class="text-gray-600">Toggle 2D / 3D</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Tab</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Zoom to fit</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">F</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Toggle grid</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">G</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Toggle layers</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">L</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Show shortcuts</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">?</kbd></div></div> <div class="flex items-center gap-2 mb-2"><span class="text-xs font-bold uppercase tracking-wider text-purple-500">Canvas</span> <div class="flex-1 h-px bg-purple-100"></div></div> <div class="space-y-1.5 mb-5"><div class="flex justify-between"><span class="text-gray-600">Zoom in / out</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Scroll</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Zoom in / out</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">+ / −</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Pan canvas</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Space+Drag</kbd></div></div> <div class="flex items-center gap-2 mb-2"><span class="text-xs font-bold uppercase tracking-wider text-rose-500">Walls</span> <div class="flex-1 h-px bg-rose-100"></div></div> <div class="space-y-1.5"><div class="flex justify-between"><span class="text-gray-600">Finish wall chain</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">Dbl-click</kbd></div> <div class="flex justify-between"><span class="text-gray-600">Close wall loop</span><kbd class="px-1.5 py-0.5 bg-gray-100 rounded text-xs font-mono text-slate-700 border border-gray-200">C</kbd></div></div></div></div></div> <div class="px-6 py-3 border-t border-gray-100 text-center"><p class="text-xs text-gray-400">Press <kbd class="px-1 py-0.5 bg-gray-100 rounded text-xs font-mono border border-gray-200">?</kbd> or <kbd class="px-1 py-0.5 bg-gray-100 rounded text-xs font-mono border border-gray-200">Esc</kbd> to close</p></div></div></div>`);
70077
- var root = /* @__PURE__ */ from_html(`<div class="h-full flex flex-col overflow-hidden"><!> <div class="flex flex-1 overflow-hidden"><!> <div class="flex-1 min-w-0 relative"><!></div> <!> <!></div></div> <!> <!> <!> <!> <!>`, 1);
70266
+ var root = /* @__PURE__ */ from_html(`<div class="h-full flex flex-col overflow-hidden"><!> <div class="flex flex-1 overflow-hidden relative"><!> <div class="flex-1 min-w-0 relative"><!></div> <!> <!></div></div> <!> <!> <!> <!> <!>`, 1);
70078
70267
  function App($$anchor, $$props) {
70079
70268
  var _$$props$stores, _$$props$stores2;
70080
70269
  push($$props, true);
@@ -70114,6 +70303,7 @@ function App($$anchor, $$props) {
70114
70303
  set(mode, m, true);
70115
70304
  });
70116
70305
  user_effect(() => {
70306
+ resetAssignedCamerasSyncState();
70117
70307
  currentProject.set(normalizeFloorData(get(floorData)));
70118
70308
  });
70119
70309
  user_effect(() => {
@@ -70156,20 +70346,29 @@ function App($$anchor, $$props) {
70156
70346
  var div = first_child(fragment);
70157
70347
  var node = child(div);
70158
70348
  var consequent = ($$anchor) => {
70159
- TopBar($$anchor, {
70160
- get viewOnly() {
70161
- return get(config).viewOnly;
70162
- },
70163
- get onDispatch() {
70164
- return $$props.onDispatch;
70165
- },
70166
- get showBuildSidebar() {
70167
- return get(showBuildSidebar);
70168
- },
70169
- set showBuildSidebar($$value) {
70170
- set(showBuildSidebar, $$value, true);
70171
- }
70172
- });
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
+ }
70173
70372
  };
70174
70373
  if_block(node, ($$render) => {
70175
70374
  if (!get(config).viewOnly) $$render(consequent);