@operato/slot-layout-editor 10.29.57 → 10.29.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,15 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ### [10.29.58](https://github.com/hatiolab/operato/compare/v10.29.57...v10.29.58) (2026-10-27)
7
+
8
+
9
+ ### :rocket: New Features
10
+
11
+ * **slot-layout-editor:** one line for a tier's slots past their face, host-set panel look ([9e7946a](https://github.com/hatiolab/operato/commit/9e7946a111e775ef40efdd88fc3d0ef5af613d8c))
12
+
13
+
14
+
6
15
  ### [10.29.57](https://github.com/hatiolab/operato/compare/v10.29.56...v10.29.57) (2026-10-27)
7
16
 
8
17
 
@@ -17,6 +17,13 @@ export declare const t: (key: string, fallback: string, opts?: Record<string, un
17
17
  /** 자리 공간 경고의 기본 문구. 번역 키는 label.slot-layout.space.{코드} */
18
18
  export declare const SPACE_TEXT: Record<SlotSpaceCode, string>;
19
19
  export declare function spaceWarningText(warning: EditorIssue): string;
20
+ /** Slot numbers as few words: runs of numbers as 1~8, the rest one by one. */
21
+ export declare function slotsWords(slots: readonly (string | number)[]): string;
22
+ /**
23
+ * The space warnings to show: the slots of one tier that reach past their face said in one line. Eight slots past a
24
+ * face were eight red lines that said the same (figure modeller sweep 2026-10-27 M3).
25
+ */
26
+ export declare function spaceWarningLines(warnings: readonly EditorIssue[]): string[];
20
27
  /** 문제 하나를 화면 문구로. 앞에 값의 위치를 붙인다 */
21
28
  export declare function issueText(issue: EditorIssue): string;
22
29
  /** 문제 하나를 화면 문구로, 위치 없이. 칸 옆에 보일 때 쓴다 */
@@ -36,6 +36,51 @@ export function spaceWarningText(warning) {
36
36
  const fallback = (_a = SPACE_TEXT[warning.code]) !== null && _a !== void 0 ? _a : warning.code;
37
37
  return t(`space.${warning.code}`, fallback, { ...warning.params, interpolation: { escapeValue: false } });
38
38
  }
39
+ /** Slot numbers as few words: runs of numbers as 1~8, the rest one by one. */
40
+ export function slotsWords(slots) {
41
+ const numbers = slots.map(Number);
42
+ if (!numbers.every(Number.isInteger))
43
+ return slots.join(', ');
44
+ const sorted = [...new Set(numbers)].sort((a, b) => a - b);
45
+ const runs = [];
46
+ for (let i = 0; i < sorted.length;) {
47
+ let j = i;
48
+ while (j + 1 < sorted.length && sorted[j + 1] === sorted[j] + 1)
49
+ j++;
50
+ runs.push(j - i >= 2 ? `${sorted[i]}~${sorted[j]}` : sorted.slice(i, j + 1).join(', '));
51
+ i = j + 1;
52
+ }
53
+ return runs.join(', ');
54
+ }
55
+ /**
56
+ * The space warnings to show: the slots of one tier that reach past their face said in one line. Eight slots past a
57
+ * face were eight red lines that said the same (figure modeller sweep 2026-10-27 M3).
58
+ */
59
+ export function spaceWarningLines(warnings) {
60
+ var _a, _b, _c, _d;
61
+ const outside = new Map();
62
+ const lines = [];
63
+ for (const w of warnings) {
64
+ if (w.code !== 'SLOT_OUTSIDE_ANCHOR') {
65
+ lines.push(spaceWarningText(w));
66
+ continue;
67
+ }
68
+ const tier = String((_b = (_a = w.params) === null || _a === void 0 ? void 0 : _a.tier) !== null && _b !== void 0 ? _b : '');
69
+ if (!outside.has(tier)) {
70
+ outside.set(tier, []);
71
+ lines.push({ tier });
72
+ }
73
+ outside.get(tier).push((_d = (_c = w.params) === null || _c === void 0 ? void 0 : _c.slot) !== null && _d !== void 0 ? _d : '');
74
+ }
75
+ return lines.map(line => {
76
+ if (typeof line === 'string')
77
+ return line;
78
+ const slots = outside.get(line.tier);
79
+ if (slots.length === 1)
80
+ return spaceWarningText({ code: 'SLOT_OUTSIDE_ANCHOR', path: '', params: { tier: line.tier, slot: slots[0] } });
81
+ return t('space.SLOT_OUTSIDE_ANCHOR_MANY', 'Tier {tier} slots {slots} reach past their face', { tier: line.tier, slots: slotsWords(slots), interpolation: { escapeValue: false } });
82
+ });
83
+ }
39
84
  /** 문제 하나를 화면 문구로. 앞에 값의 위치를 붙인다 */
40
85
  export function issueText(issue) {
41
86
  var _a;
@@ -1 +1 @@
1
- {"version":3,"file":"issue-text.js","sourceRoot":"","sources":["../../src/issue-text.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * 검증 문제(코드·경로·매개변수)를 화면 문구로. 번역 키는 label.slot-layout.issue.{코드} 이고,\n * 번역이 없으면 아래 영어 기본 문구를 쓴다. 호스트 고유 코드는 호스트가 번역을 넣거나 issueText 옵션으로 바꾼다.\n */\n\nimport { i18next } from '@operato/i18n'\nimport type { LayoutIssueCode, SlotSpaceCode } from '@hatiolab/things-scene'\n\n/** 문제 하나. 호스트 고유 코드도 같은 모양으로 온다 */\nexport interface EditorIssue {\n code: string\n path: string\n params?: Record<string, string | number>\n}\n\n/** things-scene 검증 코드의 기본 문구. 자리표시자가 중괄호 하나라서 문구에 중괄호 글자를 쓰지 않는다 */\nexport const ISSUE_TEXT: Record<LayoutIssueCode, string> = {\n TIERS_REQUIRED: 'At least one tier is required',\n MOUNT_INVALID: 'Must be on, in or hang',\n ANCHOR_REQUIRED: 'Choose a face',\n ANCHOR_UNKNOWN: 'This holder has no face {anchorId}',\n TIER_NUMBER_DUPLICATE: 'Tier {tierNo} is used twice',\n FINITE_NUMBER_REQUIRED: 'A number is required',\n POSITIVE_NUMBER_REQUIRED: 'A number greater than 0 is required',\n POSITIVE_INTEGER_REQUIRED: 'A whole number of 1 or more is required',\n ARRAY_REQUIRED: 'A list is required',\n OBJECT_REQUIRED: 'The value is not a layout',\n AXIS_INVALID: 'Must be X or Z',\n PATTERN_INVALID: 'Must be one, row, grid or listed',\n SLOTS_REQUIRED: 'At least one slot is required',\n SLOT_NUMBER_DUPLICATE: 'Slot {slotNo} is used twice',\n NOT_PERSISTABLE: 'Cannot be saved ({value})',\n ACCEPTS_EMPTY: 'The list of types it takes is empty; choose 「Any type」 to take any item'\n}\n\nexport const t = (key: string, fallback: string, opts: Record<string, unknown> = {}) =>\n i18next.t(`label.slot-layout.${key}`, { defaultValue: fallback, ...opts })\n\n/** 자리 공간 경고의 기본 문구. 번역 키는 label.slot-layout.space.{코드} */\nexport const SPACE_TEXT: Record<SlotSpaceCode, string> = {\n SLOT_OVERLAP: 'Tier {tier} slot {slot} overlaps tier {otherTier} slot {otherSlot}',\n SLOT_OUTSIDE_ANCHOR: 'Tier {tier} slot {slot} reaches past its face',\n ANCHOR_SPACE_UNKNOWN: 'Face {anchorId} has no size: whether slots stay on it is not checked'\n}\n\nexport function spaceWarningText(warning: EditorIssue): string {\n const fallback = (SPACE_TEXT as Record<string, string>)[warning.code] ?? warning.code\n return t(`space.${warning.code}`, fallback, { ...warning.params, interpolation: { escapeValue: false } })\n}\n\n/** 문제 하나를 화면 문구로. 앞에 값의 위치를 붙인다 */\nexport function issueText(issue: EditorIssue): string {\n const fallback = (ISSUE_TEXT as Record<string, string>)[issue.code] ?? issue.code\n const text = t(`issue.${issue.code}`, fallback, { ...issue.params, interpolation: { escapeValue: false } })\n return issue.path ? `${issue.path}: ${text}` : text\n}\n\n/** 문제 하나를 화면 문구로, 위치 없이. 칸 옆에 보일 때 쓴다 */\nexport function issueMessage(issue: EditorIssue): string {\n const fallback = (ISSUE_TEXT as Record<string, string>)[issue.code] ?? issue.code\n return t(`issue.${issue.code}`, fallback, { ...issue.params, interpolation: { escapeValue: false } })\n}\n\n/** Tier 안의 경로 → 편집기 칸(data-field). 칸이 없는 경로(CUSTOM 자리 표 등)는 목록에 보인다 */\nconst FIELD_OF_PATH: Record<string, string> = {\n anchorId: 'anchor',\n mount: 'mount',\n position: 'position-x',\n 'position.x': 'position-x',\n 'position.y': 'position-y',\n 'position.z': 'position-z',\n rotation: 'tier-rotation',\n layout: 'pattern',\n 'layout.pattern': 'pattern',\n 'layout.count': 'count',\n 'layout.pitch': 'pitch',\n 'layout.pitch.x': 'pitch-x',\n 'layout.pitch.z': 'pitch-z',\n 'layout.axis': 'axis',\n 'layout.origin': 'origin',\n 'layout.origin.x': 'origin-x',\n 'layout.origin.z': 'origin-z',\n 'layout.rows': 'rows',\n 'layout.cols': 'cols',\n 'layout.order': 'order',\n 'layout.slot': 'allowed-width',\n 'layout.slot.allowed': 'allowed-width',\n 'layout.slot.allowed.width': 'allowed-width',\n 'layout.slot.allowed.depth': 'allowed-depth',\n 'layout.slot.allowed.height': 'allowed-height',\n 'layout.slot.stackLimit': 'stack-limit',\n 'layout.slot.rotation': 'rotation',\n 'layout.slot.accepts': 'accepts'\n}\n\n/** 문제가 어느 Tier(목록 순서)의 어느 칸인지. Tier 밖이거나 칸이 없으면 field 없음 */\nexport function issueField(issue: EditorIssue): { tierIndex?: number; field?: string } {\n const m = /^tiers\\[(\\d+)\\](?:\\.(.+))?$/.exec(issue.path ?? '')\n if (!m) return {}\n return { tierIndex: Number(m[1]), field: m[2] ? FIELD_OF_PATH[m[2]] : undefined }\n}\n\n/** 실린 아이템 판정 까닭의 기본 문구. 번역 키는 label.slot-layout.fit.{까닭} */\nexport const FIT_TEXT: Record<string, string> = {\n SLOT_NOT_FOUND: 'Slot gone: kept, unplaced',\n TYPE_NOT_ACCEPTED: 'Type not taken here',\n WIDTH_EXCEEDED: 'Wider than the slot',\n DEPTH_EXCEEDED: 'Deeper than the slot',\n HEIGHT_EXCEEDED: 'Taller than the slot',\n STACK_LIMIT_EXCEEDED: 'Over the stack limit',\n SPEC_UNKNOWN: 'Size unknown: not judged'\n}\n\nexport function fitText(reason: string): string {\n return t(`fit.${reason}`, FIT_TEXT[reason] ?? reason)\n}\n\n/** 면에 맞출 수 없는 까닭의 기본 문구. 번역 키는 label.slot-layout.fit-face.{까닭} */\nexport const FIT_FACE_TEXT: Record<string, string> = {\n FACE_UNKNOWN: 'The face size is not known',\n PATTERN_UNSUPPORTED: 'Not for listed slots',\n ROTATION_UNSUPPORTED: 'Only for turns of 90°',\n SLOT_WIDER_THAN_FACE: 'A slot is larger than the face',\n SLOTS_DO_NOT_FIT: 'The slots do not fit on the face'\n}\n\nexport function fitFaceText(reason: string): string {\n return t(`fit-face.${reason}`, FIT_FACE_TEXT[reason] ?? reason)\n}\n"]}
1
+ {"version":3,"file":"issue-text.js","sourceRoot":"","sources":["../../src/issue-text.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * 검증 문제(코드·경로·매개변수)를 화면 문구로. 번역 키는 label.slot-layout.issue.{코드} 이고,\n * 번역이 없으면 아래 영어 기본 문구를 쓴다. 호스트 고유 코드는 호스트가 번역을 넣거나 issueText 옵션으로 바꾼다.\n */\n\nimport { i18next } from '@operato/i18n'\nimport type { LayoutIssueCode, SlotSpaceCode } from '@hatiolab/things-scene'\n\n/** 문제 하나. 호스트 고유 코드도 같은 모양으로 온다 */\nexport interface EditorIssue {\n code: string\n path: string\n params?: Record<string, string | number>\n}\n\n/** things-scene 검증 코드의 기본 문구. 자리표시자가 중괄호 하나라서 문구에 중괄호 글자를 쓰지 않는다 */\nexport const ISSUE_TEXT: Record<LayoutIssueCode, string> = {\n TIERS_REQUIRED: 'At least one tier is required',\n MOUNT_INVALID: 'Must be on, in or hang',\n ANCHOR_REQUIRED: 'Choose a face',\n ANCHOR_UNKNOWN: 'This holder has no face {anchorId}',\n TIER_NUMBER_DUPLICATE: 'Tier {tierNo} is used twice',\n FINITE_NUMBER_REQUIRED: 'A number is required',\n POSITIVE_NUMBER_REQUIRED: 'A number greater than 0 is required',\n POSITIVE_INTEGER_REQUIRED: 'A whole number of 1 or more is required',\n ARRAY_REQUIRED: 'A list is required',\n OBJECT_REQUIRED: 'The value is not a layout',\n AXIS_INVALID: 'Must be X or Z',\n PATTERN_INVALID: 'Must be one, row, grid or listed',\n SLOTS_REQUIRED: 'At least one slot is required',\n SLOT_NUMBER_DUPLICATE: 'Slot {slotNo} is used twice',\n NOT_PERSISTABLE: 'Cannot be saved ({value})',\n ACCEPTS_EMPTY: 'The list of types it takes is empty; choose 「Any type」 to take any item'\n}\n\nexport const t = (key: string, fallback: string, opts: Record<string, unknown> = {}) =>\n i18next.t(`label.slot-layout.${key}`, { defaultValue: fallback, ...opts })\n\n/** 자리 공간 경고의 기본 문구. 번역 키는 label.slot-layout.space.{코드} */\nexport const SPACE_TEXT: Record<SlotSpaceCode, string> = {\n SLOT_OVERLAP: 'Tier {tier} slot {slot} overlaps tier {otherTier} slot {otherSlot}',\n SLOT_OUTSIDE_ANCHOR: 'Tier {tier} slot {slot} reaches past its face',\n ANCHOR_SPACE_UNKNOWN: 'Face {anchorId} has no size: whether slots stay on it is not checked'\n}\n\nexport function spaceWarningText(warning: EditorIssue): string {\n const fallback = (SPACE_TEXT as Record<string, string>)[warning.code] ?? warning.code\n return t(`space.${warning.code}`, fallback, { ...warning.params, interpolation: { escapeValue: false } })\n}\n\n/** Slot numbers as few words: runs of numbers as 1~8, the rest one by one. */\nexport function slotsWords(slots: readonly (string | number)[]): string {\n const numbers = slots.map(Number)\n if (!numbers.every(Number.isInteger)) return slots.join(', ')\n const sorted = [...new Set(numbers)].sort((a, b) => a - b)\n const runs: string[] = []\n for (let i = 0; i < sorted.length; ) {\n let j = i\n while (j + 1 < sorted.length && sorted[j + 1] === sorted[j]! + 1) j++\n runs.push(j - i >= 2 ? `${sorted[i]}~${sorted[j]}` : sorted.slice(i, j + 1).join(', '))\n i = j + 1\n }\n return runs.join(', ')\n}\n\n/**\n * The space warnings to show: the slots of one tier that reach past their face said in one line. Eight slots past a\n * face were eight red lines that said the same (figure modeller sweep 2026-10-27 M3).\n */\nexport function spaceWarningLines(warnings: readonly EditorIssue[]): string[] {\n const outside = new Map<string, (string | number)[]>()\n const lines: (string | { tier: string })[] = []\n for (const w of warnings) {\n if (w.code !== 'SLOT_OUTSIDE_ANCHOR') {\n lines.push(spaceWarningText(w))\n continue\n }\n const tier = String(w.params?.tier ?? '')\n if (!outside.has(tier)) {\n outside.set(tier, [])\n lines.push({ tier })\n }\n outside.get(tier)!.push(w.params?.slot ?? '')\n }\n return lines.map(line => {\n if (typeof line === 'string') return line\n const slots = outside.get(line.tier)!\n if (slots.length === 1) return spaceWarningText({ code: 'SLOT_OUTSIDE_ANCHOR', path: '', params: { tier: line.tier, slot: slots[0]! } })\n return t('space.SLOT_OUTSIDE_ANCHOR_MANY', 'Tier {tier} slots {slots} reach past their face', { tier: line.tier, slots: slotsWords(slots), interpolation: { escapeValue: false } })\n })\n}\n\n/** 문제 하나를 화면 문구로. 앞에 값의 위치를 붙인다 */\nexport function issueText(issue: EditorIssue): string {\n const fallback = (ISSUE_TEXT as Record<string, string>)[issue.code] ?? issue.code\n const text = t(`issue.${issue.code}`, fallback, { ...issue.params, interpolation: { escapeValue: false } })\n return issue.path ? `${issue.path}: ${text}` : text\n}\n\n/** 문제 하나를 화면 문구로, 위치 없이. 칸 옆에 보일 때 쓴다 */\nexport function issueMessage(issue: EditorIssue): string {\n const fallback = (ISSUE_TEXT as Record<string, string>)[issue.code] ?? issue.code\n return t(`issue.${issue.code}`, fallback, { ...issue.params, interpolation: { escapeValue: false } })\n}\n\n/** Tier 안의 경로 → 편집기 칸(data-field). 칸이 없는 경로(CUSTOM 자리 표 등)는 목록에 보인다 */\nconst FIELD_OF_PATH: Record<string, string> = {\n anchorId: 'anchor',\n mount: 'mount',\n position: 'position-x',\n 'position.x': 'position-x',\n 'position.y': 'position-y',\n 'position.z': 'position-z',\n rotation: 'tier-rotation',\n layout: 'pattern',\n 'layout.pattern': 'pattern',\n 'layout.count': 'count',\n 'layout.pitch': 'pitch',\n 'layout.pitch.x': 'pitch-x',\n 'layout.pitch.z': 'pitch-z',\n 'layout.axis': 'axis',\n 'layout.origin': 'origin',\n 'layout.origin.x': 'origin-x',\n 'layout.origin.z': 'origin-z',\n 'layout.rows': 'rows',\n 'layout.cols': 'cols',\n 'layout.order': 'order',\n 'layout.slot': 'allowed-width',\n 'layout.slot.allowed': 'allowed-width',\n 'layout.slot.allowed.width': 'allowed-width',\n 'layout.slot.allowed.depth': 'allowed-depth',\n 'layout.slot.allowed.height': 'allowed-height',\n 'layout.slot.stackLimit': 'stack-limit',\n 'layout.slot.rotation': 'rotation',\n 'layout.slot.accepts': 'accepts'\n}\n\n/** 문제가 어느 Tier(목록 순서)의 어느 칸인지. Tier 밖이거나 칸이 없으면 field 없음 */\nexport function issueField(issue: EditorIssue): { tierIndex?: number; field?: string } {\n const m = /^tiers\\[(\\d+)\\](?:\\.(.+))?$/.exec(issue.path ?? '')\n if (!m) return {}\n return { tierIndex: Number(m[1]), field: m[2] ? FIELD_OF_PATH[m[2]] : undefined }\n}\n\n/** 실린 아이템 판정 까닭의 기본 문구. 번역 키는 label.slot-layout.fit.{까닭} */\nexport const FIT_TEXT: Record<string, string> = {\n SLOT_NOT_FOUND: 'Slot gone: kept, unplaced',\n TYPE_NOT_ACCEPTED: 'Type not taken here',\n WIDTH_EXCEEDED: 'Wider than the slot',\n DEPTH_EXCEEDED: 'Deeper than the slot',\n HEIGHT_EXCEEDED: 'Taller than the slot',\n STACK_LIMIT_EXCEEDED: 'Over the stack limit',\n SPEC_UNKNOWN: 'Size unknown: not judged'\n}\n\nexport function fitText(reason: string): string {\n return t(`fit.${reason}`, FIT_TEXT[reason] ?? reason)\n}\n\n/** 면에 맞출 수 없는 까닭의 기본 문구. 번역 키는 label.slot-layout.fit-face.{까닭} */\nexport const FIT_FACE_TEXT: Record<string, string> = {\n FACE_UNKNOWN: 'The face size is not known',\n PATTERN_UNSUPPORTED: 'Not for listed slots',\n ROTATION_UNSUPPORTED: 'Only for turns of 90°',\n SLOT_WIDER_THAN_FACE: 'A slot is larger than the face',\n SLOTS_DO_NOT_FIT: 'The slots do not fit on the face'\n}\n\nexport function fitFaceText(reason: string): string {\n return t(`fit-face.${reason}`, FIT_FACE_TEXT[reason] ?? reason)\n}\n"]}
@@ -19,7 +19,7 @@ import { customElement, property, state } from 'lit/decorators.js';
19
19
  import { i18next, localize } from '@operato/i18n';
20
20
  import { persistenceIssues, rotatedFootprint, slotSpaceWarnings, validateSlotLayout } from '@hatiolab/things-scene';
21
21
  import { addCustomSlot, addressImpact, anchorFaceSlots, applyPreset, copyTier, fitTierToFace, heldItemStatuses, pitchToPoint, removeCustomSlot, removeTier, setTierPattern, updatePattern, updateSlotSpec, updateTier } from './draft.js';
22
- import { fitFaceText, fitText, issueField, issueMessage, issueText as defaultIssueText, ISSUE_TEXT, spaceWarningText, t } from './issue-text.js';
22
+ import { fitFaceText, fitText, issueField, issueMessage, issueText as defaultIssueText, ISSUE_TEXT, spaceWarningLines, t } from './issue-text.js';
23
23
  const num = (e) => {
24
24
  const n = e.target.valueAsNumber;
25
25
  return Number.isFinite(n) ? n : NaN;
@@ -372,8 +372,10 @@ let OxSlotLayoutEditor = class OxSlotLayoutEditor extends localize(i18next)(LitE
372
372
  const a = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.anchors) === null || _b === void 0 ? void 0 : _b.find(x => x.id === id);
373
373
  if (a === null || a === void 0 ? void 0 : a.label)
374
374
  return a.label;
375
+ // 면 이름이 이미 부품을 말한다: id 가 부품 이름과 같으면 다시 적지 않는다(「tub · tub 윗면」, figure 점검 2026-10-27 M5)
376
+ const face = t(`face-${(_c = a === null || a === void 0 ? void 0 : a.face) !== null && _c !== void 0 ? _c : 'top'}`, `{part} ${(_d = a === null || a === void 0 ? void 0 : a.face) !== null && _d !== void 0 ? _d : 'top'}`, { part: a === null || a === void 0 ? void 0 : a.part });
375
377
  if (a === null || a === void 0 ? void 0 : a.part)
376
- return `${id} · ${t(`face-${(_c = a.face) !== null && _c !== void 0 ? _c : 'top'}`, `{part} ${(_d = a.face) !== null && _d !== void 0 ? _d : 'top'}`, { part: a.part })}`;
378
+ return a.part === id ? face : `${id} · ${face}`;
377
379
  return id;
378
380
  }
379
381
  _unit() {
@@ -478,7 +480,7 @@ let OxSlotLayoutEditor = class OxSlotLayoutEditor extends localize(i18next)(LitE
478
480
  const warnings = this.warnings.filter(w => { var _a; return !(w.code === 'ANCHOR_SPACE_UNKNOWN' && !faceKnown && ((_a = w.params) === null || _a === void 0 ? void 0 : _a.anchorId) === anchorId); });
479
481
  if (!warnings.length)
480
482
  return nothing;
481
- return html `<div class="error" data-warnings>${warnings.map(w => html `<div>· ${spaceWarningText(w)}</div>`)}</div>`;
483
+ return html `<div class="error" data-warnings>${spaceWarningLines(warnings).map(line => html `<div>· ${line}</div>`)}</div>`;
482
484
  }
483
485
  /**
484
486
  * 실린 아이템이 편집 중인 레이아웃에서 어떻게 되는지. 자리가 없어지면 미배치로 남고(지우거나 옮기지 않는다), 받는 타입·규격은
@@ -794,18 +796,24 @@ let OxSlotLayoutEditor = class OxSlotLayoutEditor extends localize(i18next)(LitE
794
796
  `;
795
797
  }
796
798
  };
799
+ /*
800
+ 호스트가 자기 패널의 모양을 줄 수 있다(figure 점검 2026-10-27 M2): 글자 크기, 라벨 열과 정렬, 입력칸의 높이·테두리.
801
+ --slot-layout-font-size · --slot-layout-label-column · --slot-layout-label-align ·
802
+ --slot-layout-field-border · --slot-layout-field-border-bottom · --slot-layout-field-radius · --slot-layout-field-background.
803
+ 주지 않으면 지금 모양 그대로다(--property-editor-* 를 따른다).
804
+ */
797
805
  OxSlotLayoutEditor.styles = css `
798
806
  :host {
799
807
  display: flex;
800
808
  flex-direction: column;
801
809
  gap: 8px;
802
810
  min-width: 0;
803
- font-size: 12px;
811
+ font-size: var(--slot-layout-font-size, 12px);
804
812
  color: var(--property-editor-color, inherit);
805
813
  }
806
814
  section {
807
815
  display: grid;
808
- grid-template-columns: minmax(64px, 34%) minmax(0, 1fr);
816
+ grid-template-columns: var(--slot-layout-label-column, minmax(64px, 34%)) minmax(0, 1fr);
809
817
  gap: 4px 6px;
810
818
  align-items: center;
811
819
  }
@@ -813,7 +821,7 @@ OxSlotLayoutEditor.styles = css `
813
821
  summary {
814
822
  grid-column: 1 / -1;
815
823
  margin: 4px 0 0;
816
- font-size: 12px;
824
+ font-size: var(--slot-layout-font-size, 12px);
817
825
  font-weight: bold;
818
826
  }
819
827
  summary {
@@ -823,7 +831,7 @@ OxSlotLayoutEditor.styles = css `
823
831
  margin-top: 4px;
824
832
  }
825
833
  label.l {
826
- text-align: right;
834
+ text-align: var(--slot-layout-label-align, right);
827
835
  font: var(--property-editor-label-font, inherit);
828
836
  color: var(--property-editor-label-color, inherit);
829
837
  }
@@ -858,9 +866,10 @@ OxSlotLayoutEditor.styles = css `
858
866
  min-width: 0;
859
867
  min-height: var(--property-editor-field-height, 24px);
860
868
  padding: var(--property-editor-field-padding, 1px 4px);
861
- border: var(--property-editor-border, 1px solid rgba(0, 0, 0, 0.2));
862
- border-radius: var(--property-editor-border-radius, 4px);
863
- background-color: var(--property-editor-background, field);
869
+ border: var(--slot-layout-field-border, var(--property-editor-border, 1px solid rgba(0, 0, 0, 0.2)));
870
+ border-bottom: var(--slot-layout-field-border-bottom, var(--slot-layout-field-border, var(--property-editor-border, 1px solid rgba(0, 0, 0, 0.2))));
871
+ border-radius: var(--slot-layout-field-radius, var(--property-editor-border-radius, 4px));
872
+ background-color: var(--slot-layout-field-background, var(--property-editor-background, field));
864
873
  color: inherit;
865
874
  font: var(--property-editor-field-font, inherit);
866
875
  outline: none;