@dmitryvim/form-builder 0.2.19 → 0.2.21

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.
@@ -223,6 +223,11 @@ function pathJoin(base, key) {
223
223
  function clear(node) {
224
224
  while (node.firstChild) node.removeChild(node.firstChild);
225
225
  }
226
+ function formatFileSize(bytes) {
227
+ if (bytes < 1024) return `${bytes} B`;
228
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
229
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
230
+ }
226
231
 
227
232
  // src/utils/enable-conditions.ts
228
233
  function getValueByPath(data, path) {
@@ -4773,18 +4778,54 @@ function updateGroupField(element, fieldPath, value, context) {
4773
4778
  }
4774
4779
 
4775
4780
  // src/components/table.ts
4781
+ function isLegacyMerge(m) {
4782
+ return m !== null && typeof m === "object" && "row" in m && "col" in m && "rowspan" in m && "colspan" in m;
4783
+ }
4784
+ function isValidMergeShape(m) {
4785
+ if (m === null || typeof m !== "object") return false;
4786
+ const o = m;
4787
+ return typeof o.top === "number" && typeof o.left === "number" && typeof o.bottom === "number" && typeof o.right === "number";
4788
+ }
4789
+ function migrateMerge(m) {
4790
+ if (isLegacyMerge(m)) {
4791
+ return {
4792
+ top: m.row,
4793
+ left: m.col,
4794
+ bottom: m.row + m.rowspan - 1,
4795
+ right: m.col + m.colspan - 1
4796
+ };
4797
+ }
4798
+ if (isValidMergeShape(m)) return m;
4799
+ return null;
4800
+ }
4801
+ function migrateMerges(merges) {
4802
+ return merges.map(migrateMerge).filter((m) => m !== null);
4803
+ }
4776
4804
  function createEmptyCells(rows, cols) {
4777
4805
  return Array.from(
4778
4806
  { length: rows },
4779
4807
  () => Array.from({ length: cols }, () => "")
4780
4808
  );
4781
4809
  }
4810
+ function validateMerges(merges, rows, cols) {
4811
+ for (let i = 0; i < merges.length; i++) {
4812
+ const a = merges[i];
4813
+ if (a.top < 0 || a.left < 0 || a.bottom >= rows || a.right >= cols || a.top > a.bottom || a.left > a.right)
4814
+ return `Merge ${i} out of bounds`;
4815
+ for (let j = i + 1; j < merges.length; j++) {
4816
+ const b = merges[j];
4817
+ if (a.top <= b.bottom && a.bottom >= b.top && a.left <= b.right && a.right >= b.left)
4818
+ return `Merges ${i} and ${j} overlap`;
4819
+ }
4820
+ }
4821
+ return null;
4822
+ }
4782
4823
  function getShadowingMerge(row, col, merges) {
4783
4824
  for (const m of merges) {
4784
- if (m.row === row && m.col === col) {
4825
+ if (m.top === row && m.left === col) {
4785
4826
  return null;
4786
4827
  }
4787
- if (row >= m.row && row < m.row + m.rowspan && col >= m.col && col < m.col + m.colspan) {
4828
+ if (row >= m.top && row <= m.bottom && col >= m.left && col <= m.right) {
4788
4829
  return m;
4789
4830
  }
4790
4831
  }
@@ -4792,7 +4833,7 @@ function getShadowingMerge(row, col, merges) {
4792
4833
  }
4793
4834
  function getMergeAt(row, col, merges) {
4794
4835
  var _a;
4795
- return (_a = merges.find((m) => m.row === row && m.col === col)) != null ? _a : null;
4836
+ return (_a = merges.find((m) => m.top === row && m.left === col)) != null ? _a : null;
4796
4837
  }
4797
4838
  function selectionRange(sel) {
4798
4839
  var _a;
@@ -4868,8 +4909,10 @@ function renderReadonlyTable(data, wrapper) {
4868
4909
  const merge = getMergeAt(rIdx, cIdx, merges);
4869
4910
  const td = document.createElement(rIdx === 0 ? "th" : "td");
4870
4911
  if (merge) {
4871
- if (merge.rowspan > 1) td.rowSpan = merge.rowspan;
4872
- if (merge.colspan > 1) td.colSpan = merge.colspan;
4912
+ const rowspan = merge.bottom - merge.top + 1;
4913
+ const colspan = merge.right - merge.left + 1;
4914
+ if (rowspan > 1) td.rowSpan = rowspan;
4915
+ if (colspan > 1) td.colSpan = colspan;
4873
4916
  }
4874
4917
  td.textContent = (_b = rowData[cIdx]) != null ? _b : "";
4875
4918
  td.style.cssText = `
@@ -4956,22 +4999,56 @@ function startCellEditing(span, r, c, getCells, persistValue, selectCell) {
4956
4999
  span.addEventListener("keydown", onKeyDown);
4957
5000
  span.addEventListener("blur", onBlur);
4958
5001
  }
5002
+ function ensureSpinKeyframes() {
5003
+ if (document.getElementById("fb-spin-keyframes")) return;
5004
+ const style = document.createElement("style");
5005
+ style.id = "fb-spin-keyframes";
5006
+ style.textContent = `@keyframes fb-spin { to { transform: rotate(360deg); } }`;
5007
+ document.head.appendChild(style);
5008
+ }
5009
+ function showLoadingOverlay(parent, text) {
5010
+ ensureSpinKeyframes();
5011
+ const overlay = document.createElement("div");
5012
+ overlay.className = "fb-table-loading-overlay";
5013
+ overlay.style.cssText = `
5014
+ position: absolute; top: 0; left: 0; right: 0; bottom: 0;
5015
+ background: rgba(255,255,255,0.8); display: flex;
5016
+ align-items: center; justify-content: center; flex-direction: column;
5017
+ gap: 8px; z-index: 100;
5018
+ `;
5019
+ const spinner = document.createElement("div");
5020
+ spinner.style.cssText = `
5021
+ width: 24px; height: 24px; border: 3px solid var(--fb-border-color, #ccc);
5022
+ border-top-color: var(--fb-primary-color, #0066cc); border-radius: 50%;
5023
+ animation: fb-spin 0.8s linear infinite;
5024
+ `;
5025
+ const label = document.createElement("span");
5026
+ label.textContent = text;
5027
+ label.style.cssText = `font-size: var(--fb-font-size-small, 12px); color: var(--fb-text-color, #333);`;
5028
+ overlay.appendChild(spinner);
5029
+ overlay.appendChild(label);
5030
+ parent.appendChild(overlay);
5031
+ return overlay;
5032
+ }
4959
5033
  function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
4960
- var _a, _b;
5034
+ var _a, _b, _c, _d, _e, _f, _g, _h;
4961
5035
  const state = ctx.state;
4962
5036
  const instance = ctx.instance;
4963
- const cells = initialData.cells.length > 0 ? initialData.cells.map((r) => [...r]) : createEmptyCells((_a = element.rows) != null ? _a : 3, (_b = element.columns) != null ? _b : 3);
5037
+ const mergeAllowed = element.mergeAllowed !== false;
5038
+ const cellsKey = (_b = (_a = element.fieldNames) == null ? void 0 : _a.cells) != null ? _b : "cells";
5039
+ const mergesKey = (_d = (_c = element.fieldNames) == null ? void 0 : _c.merges) != null ? _d : "merges";
5040
+ const cells = initialData.cells.length > 0 ? initialData.cells.map((r) => [...r]) : createEmptyCells((_e = element.rows) != null ? _e : 3, (_f = element.columns) != null ? _f : 3);
4964
5041
  let merges = initialData.merges ? [...initialData.merges] : [];
4965
5042
  const sel = { anchor: null, focus: null, dragging: false };
4966
5043
  const hiddenInput = document.createElement("input");
4967
5044
  hiddenInput.type = "hidden";
4968
5045
  hiddenInput.name = pathKey;
4969
- hiddenInput.value = JSON.stringify({ cells, merges });
5046
+ hiddenInput.value = JSON.stringify({ [cellsKey]: cells, [mergesKey]: merges });
4970
5047
  wrapper.appendChild(hiddenInput);
4971
5048
  function persistValue() {
4972
- hiddenInput.value = JSON.stringify({ cells, merges });
5049
+ hiddenInput.value = JSON.stringify({ [cellsKey]: cells, [mergesKey]: merges });
4973
5050
  if (instance) {
4974
- instance.triggerOnChange(pathKey, { cells, merges });
5051
+ instance.triggerOnChange(pathKey, { [cellsKey]: cells, [mergesKey]: merges });
4975
5052
  }
4976
5053
  }
4977
5054
  hiddenInput._applyExternalUpdate = (data) => {
@@ -4997,6 +5074,116 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
4997
5074
  table-layout: fixed;
4998
5075
  `;
4999
5076
  tableWrapper.appendChild(tableEl);
5077
+ const acceptExts = (_h = (_g = element.importAccept) == null ? void 0 : _g.map((ext) => `.${ext.toLowerCase()}`)) != null ? _h : [];
5078
+ async function importFile(file) {
5079
+ var _a2, _b2, _c2;
5080
+ if (!state.config.parseTableFile) return;
5081
+ if (acceptExts.length > 0) {
5082
+ const ext = file.name.toLowerCase().replace(/^.*(\.[^.]+)$/, "$1");
5083
+ if (!acceptExts.includes(ext)) return;
5084
+ }
5085
+ const overlay = showLoadingOverlay(
5086
+ tableWrapper,
5087
+ t("tableImporting", state)
5088
+ );
5089
+ try {
5090
+ const result = await state.config.parseTableFile(file);
5091
+ const importedCells = result.cells;
5092
+ const importedMerges = (_a2 = result.merges) != null ? _a2 : [];
5093
+ if (importedMerges.length > 0) {
5094
+ const err = validateMerges(
5095
+ importedMerges,
5096
+ importedCells.length,
5097
+ (_c2 = (_b2 = importedCells[0]) == null ? void 0 : _b2.length) != null ? _c2 : 0
5098
+ );
5099
+ if (err) throw new Error(err);
5100
+ }
5101
+ cells.length = 0;
5102
+ importedCells.forEach((row) => cells.push([...row]));
5103
+ merges.length = 0;
5104
+ importedMerges.forEach((m) => merges.push({ ...m }));
5105
+ sel.anchor = null;
5106
+ sel.focus = null;
5107
+ persistValue();
5108
+ rebuild();
5109
+ } catch (e) {
5110
+ const errMsg = e instanceof Error ? e.message : String(e);
5111
+ console.error(
5112
+ t("tableImportError", state).replace("{error}", errMsg)
5113
+ );
5114
+ } finally {
5115
+ overlay.remove();
5116
+ }
5117
+ }
5118
+ if (element.importAccept && state.config.parseTableFile) {
5119
+ const importBtn = document.createElement("button");
5120
+ importBtn.type = "button";
5121
+ importBtn.title = t("tableImportFile", state);
5122
+ importBtn.innerHTML = `<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="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>`;
5123
+ importBtn.style.cssText = `
5124
+ position: absolute; top: 2px; left: 2px;
5125
+ width: 20px; height: 20px;
5126
+ padding: 0;
5127
+ display: flex; align-items: center; justify-content: center;
5128
+ color: var(--fb-text-color, #999);
5129
+ border: none;
5130
+ background: transparent;
5131
+ cursor: pointer;
5132
+ z-index: 2;
5133
+ `;
5134
+ importBtn.addEventListener("mouseenter", () => {
5135
+ importBtn.style.color = "var(--fb-primary-color, #0066cc)";
5136
+ });
5137
+ importBtn.addEventListener("mouseleave", () => {
5138
+ importBtn.style.color = "var(--fb-text-color, #999)";
5139
+ });
5140
+ const importInput = document.createElement("input");
5141
+ importInput.type = "file";
5142
+ importInput.accept = acceptExts.join(",");
5143
+ importInput.style.display = "none";
5144
+ tableWrapper.appendChild(importInput);
5145
+ importBtn.addEventListener("click", () => {
5146
+ importInput.click();
5147
+ });
5148
+ importInput.addEventListener("change", () => {
5149
+ var _a2;
5150
+ const file = (_a2 = importInput.files) == null ? void 0 : _a2[0];
5151
+ if (file) importFile(file);
5152
+ importInput.value = "";
5153
+ });
5154
+ tableWrapper.appendChild(importBtn);
5155
+ let dragCounter = 0;
5156
+ tableWrapper.addEventListener("dragenter", (e) => {
5157
+ e.preventDefault();
5158
+ dragCounter++;
5159
+ if (dragCounter === 1) {
5160
+ tableWrapper.style.outline = "2px dashed var(--fb-primary-color, #0066cc)";
5161
+ tableWrapper.style.outlineOffset = "-2px";
5162
+ }
5163
+ });
5164
+ tableWrapper.addEventListener("dragover", (e) => {
5165
+ e.preventDefault();
5166
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
5167
+ });
5168
+ tableWrapper.addEventListener("dragleave", (e) => {
5169
+ e.preventDefault();
5170
+ dragCounter--;
5171
+ if (dragCounter <= 0) {
5172
+ dragCounter = 0;
5173
+ tableWrapper.style.outline = "";
5174
+ tableWrapper.style.outlineOffset = "";
5175
+ }
5176
+ });
5177
+ tableWrapper.addEventListener("drop", (e) => {
5178
+ var _a2, _b2;
5179
+ e.preventDefault();
5180
+ dragCounter = 0;
5181
+ tableWrapper.style.outline = "";
5182
+ tableWrapper.style.outlineOffset = "";
5183
+ const file = (_b2 = (_a2 = e.dataTransfer) == null ? void 0 : _a2.files) == null ? void 0 : _b2[0];
5184
+ if (file) importFile(file);
5185
+ });
5186
+ }
5000
5187
  wrapper.appendChild(tableWrapper);
5001
5188
  const contextMenu = document.createElement("div");
5002
5189
  contextMenu.style.cssText = `
@@ -5040,6 +5227,7 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5040
5227
  return btn;
5041
5228
  }
5042
5229
  function showContextMenu(x, y) {
5230
+ if (!mergeAllowed) return;
5043
5231
  contextMenu.innerHTML = "";
5044
5232
  contextMenu.style.display = "flex";
5045
5233
  const range = selectionRange(sel);
@@ -5123,11 +5311,11 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5123
5311
  const insertAt = afterIndex !== void 0 ? afterIndex + 1 : cells.length;
5124
5312
  cells.splice(insertAt, 0, newRow);
5125
5313
  merges = merges.map((m) => {
5126
- if (m.row >= insertAt) {
5127
- return { ...m, row: m.row + 1 };
5314
+ if (m.top >= insertAt) {
5315
+ return { ...m, top: m.top + 1, bottom: m.bottom + 1 };
5128
5316
  }
5129
- if (m.row < insertAt && m.row + m.rowspan > insertAt) {
5130
- return { ...m, rowspan: m.rowspan + 1 };
5317
+ if (m.top < insertAt && m.bottom >= insertAt) {
5318
+ return { ...m, bottom: m.bottom + 1 };
5131
5319
  }
5132
5320
  return m;
5133
5321
  });
@@ -5138,19 +5326,18 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5138
5326
  if (cells.length <= 1) return;
5139
5327
  const rowToRemove = targetRow !== void 0 ? targetRow : sel.anchor ? sel.anchor.row : cells.length - 1;
5140
5328
  merges = merges.map((m) => {
5141
- const mEndRow = m.row + m.rowspan - 1;
5142
- if (m.row === rowToRemove && m.rowspan === 1) return null;
5143
- if (m.row === rowToRemove) {
5144
- return { ...m, row: m.row + 1, rowspan: m.rowspan - 1 };
5329
+ if (m.top === rowToRemove && m.bottom === rowToRemove) return null;
5330
+ if (m.top === rowToRemove) {
5331
+ return { ...m, bottom: m.bottom - 1 };
5145
5332
  }
5146
- if (mEndRow === rowToRemove) {
5147
- return { ...m, rowspan: m.rowspan - 1 };
5333
+ if (m.bottom === rowToRemove) {
5334
+ return { ...m, bottom: m.bottom - 1 };
5148
5335
  }
5149
- if (m.row < rowToRemove && mEndRow > rowToRemove) {
5150
- return { ...m, rowspan: m.rowspan - 1 };
5336
+ if (m.top < rowToRemove && m.bottom > rowToRemove) {
5337
+ return { ...m, bottom: m.bottom - 1 };
5151
5338
  }
5152
- if (m.row > rowToRemove) {
5153
- return { ...m, row: m.row - 1 };
5339
+ if (m.top > rowToRemove) {
5340
+ return { ...m, top: m.top - 1, bottom: m.bottom - 1 };
5154
5341
  }
5155
5342
  return m;
5156
5343
  }).filter((m) => m !== null);
@@ -5166,11 +5353,11 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5166
5353
  const insertAt = afterIndex !== void 0 ? afterIndex + 1 : (_b2 = (_a2 = cells[0]) == null ? void 0 : _a2.length) != null ? _b2 : 0;
5167
5354
  cells.forEach((row) => row.splice(insertAt, 0, ""));
5168
5355
  merges = merges.map((m) => {
5169
- if (m.col >= insertAt) {
5170
- return { ...m, col: m.col + 1 };
5356
+ if (m.left >= insertAt) {
5357
+ return { ...m, left: m.left + 1, right: m.right + 1 };
5171
5358
  }
5172
- if (m.col < insertAt && m.col + m.colspan > insertAt) {
5173
- return { ...m, colspan: m.colspan + 1 };
5359
+ if (m.left < insertAt && m.right >= insertAt) {
5360
+ return { ...m, right: m.right + 1 };
5174
5361
  }
5175
5362
  return m;
5176
5363
  });
@@ -5181,19 +5368,18 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5181
5368
  if (cells.length === 0 || cells[0].length <= 1) return;
5182
5369
  const colToRemove = targetCol !== void 0 ? targetCol : sel.anchor ? sel.anchor.col : cells[0].length - 1;
5183
5370
  merges = merges.map((m) => {
5184
- const mEndCol = m.col + m.colspan - 1;
5185
- if (m.col === colToRemove && m.colspan === 1) return null;
5186
- if (m.col === colToRemove) {
5187
- return { ...m, col: m.col + 1, colspan: m.colspan - 1 };
5371
+ if (m.left === colToRemove && m.right === colToRemove) return null;
5372
+ if (m.left === colToRemove) {
5373
+ return { ...m, right: m.right - 1 };
5188
5374
  }
5189
- if (mEndCol === colToRemove) {
5190
- return { ...m, colspan: m.colspan - 1 };
5375
+ if (m.right === colToRemove) {
5376
+ return { ...m, right: m.right - 1 };
5191
5377
  }
5192
- if (m.col < colToRemove && mEndCol > colToRemove) {
5193
- return { ...m, colspan: m.colspan - 1 };
5378
+ if (m.left < colToRemove && m.right > colToRemove) {
5379
+ return { ...m, right: m.right - 1 };
5194
5380
  }
5195
- if (m.col > colToRemove) {
5196
- return { ...m, col: m.col - 1 };
5381
+ if (m.left > colToRemove) {
5382
+ return { ...m, left: m.left - 1, right: m.right - 1 };
5197
5383
  }
5198
5384
  return m;
5199
5385
  }).filter((m) => m !== null);
@@ -5205,14 +5391,14 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5205
5391
  rebuild();
5206
5392
  }
5207
5393
  function mergeCells() {
5394
+ var _a2, _b2;
5395
+ if (!mergeAllowed) return;
5208
5396
  const range = selectionRange(sel);
5209
5397
  if (!range) return;
5210
5398
  const { r1, c1, r2, c2 } = range;
5211
5399
  if (r1 === r2 && c1 === c2) return;
5212
5400
  merges = merges.filter((m) => {
5213
- const mEndRow = m.row + m.rowspan - 1;
5214
- const mEndCol = m.col + m.colspan - 1;
5215
- const overlaps = m.row <= r2 && mEndRow >= r1 && m.col <= c2 && mEndCol >= c1;
5401
+ const overlaps = m.top <= r2 && m.bottom >= r1 && m.left <= c2 && m.right >= c1;
5216
5402
  return !overlaps;
5217
5403
  });
5218
5404
  const anchorText = cells[r1][c1];
@@ -5224,16 +5410,24 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5224
5410
  }
5225
5411
  }
5226
5412
  cells[r1][c1] = anchorText;
5227
- merges.push({ row: r1, col: c1, rowspan: r2 - r1 + 1, colspan: c2 - c1 + 1 });
5413
+ const newMerge = { top: r1, left: c1, bottom: r2, right: c2 };
5414
+ const testMerges = [...merges, newMerge];
5415
+ const err = validateMerges(testMerges, cells.length, (_b2 = (_a2 = cells[0]) == null ? void 0 : _a2.length) != null ? _b2 : 0);
5416
+ if (err) {
5417
+ console.warn("Merge validation failed:", err);
5418
+ return;
5419
+ }
5420
+ merges.push(newMerge);
5228
5421
  sel.anchor = { row: r1, col: c1 };
5229
5422
  sel.focus = null;
5230
5423
  persistValue();
5231
5424
  rebuild();
5232
5425
  }
5233
5426
  function splitCell() {
5427
+ if (!mergeAllowed) return;
5234
5428
  if (!sel.anchor) return;
5235
5429
  const { row, col } = sel.anchor;
5236
- const mIdx = merges.findIndex((m) => m.row === row && m.col === col);
5430
+ const mIdx = merges.findIndex((m) => m.top === row && m.left === col);
5237
5431
  if (mIdx === -1) return;
5238
5432
  merges.splice(mIdx, 1);
5239
5433
  sel.focus = null;
@@ -5258,8 +5452,10 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5258
5452
  td.setAttribute("data-row", String(rIdx));
5259
5453
  td.setAttribute("data-col", String(cIdx));
5260
5454
  if (merge) {
5261
- if (merge.rowspan > 1) td.rowSpan = merge.rowspan;
5262
- if (merge.colspan > 1) td.colSpan = merge.colspan;
5455
+ const rowspan = merge.bottom - merge.top + 1;
5456
+ const colspan = merge.right - merge.left + 1;
5457
+ if (rowspan > 1) td.rowSpan = rowspan;
5458
+ if (colspan > 1) td.colSpan = colspan;
5263
5459
  }
5264
5460
  const inRange = range !== null && rIdx >= range.r1 && rIdx <= range.r2 && cIdx >= range.c1 && cIdx <= range.c2;
5265
5461
  const isAnchor = sel.anchor !== null && sel.anchor.row === rIdx && sel.anchor.col === cIdx;
@@ -5322,6 +5518,7 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5322
5518
  }
5323
5519
  });
5324
5520
  td.addEventListener("contextmenu", (e) => {
5521
+ if (!mergeAllowed) return;
5325
5522
  const currentRange = selectionRange(sel);
5326
5523
  const isMulti = currentRange && (currentRange.r1 !== currentRange.r2 || currentRange.c1 !== currentRange.c2);
5327
5524
  const isMerged = sel.anchor && getMergeAt(sel.anchor.row, sel.anchor.col, merges);
@@ -5383,18 +5580,18 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5383
5580
  selectCell(nr, nc);
5384
5581
  return;
5385
5582
  }
5386
- if (e.key === "m" && e.ctrlKey && !e.shiftKey) {
5583
+ if (mergeAllowed && e.key === "m" && e.ctrlKey && !e.shiftKey) {
5387
5584
  e.preventDefault();
5388
5585
  mergeCells();
5389
5586
  return;
5390
5587
  }
5391
- if (e.key === "M" && e.ctrlKey && e.shiftKey) {
5588
+ if (mergeAllowed && e.key === "M" && e.ctrlKey && e.shiftKey) {
5392
5589
  e.preventDefault();
5393
5590
  splitCell();
5394
5591
  }
5395
5592
  };
5396
5593
  tableEl.oncopy = (e) => {
5397
- var _a3, _b3, _c, _d;
5594
+ var _a3, _b3, _c2, _d2;
5398
5595
  const range2 = selectionRange(sel);
5399
5596
  if (!range2) return;
5400
5597
  e.preventDefault();
@@ -5415,11 +5612,11 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5415
5612
  }
5416
5613
  const tsvText = tsvRows.join("\n");
5417
5614
  const htmlText = `<table>${htmlRows.join("")}</table>`;
5418
- (_c = e.clipboardData) == null ? void 0 : _c.setData("text/plain", tsvText);
5419
- (_d = e.clipboardData) == null ? void 0 : _d.setData("text/html", htmlText);
5615
+ (_c2 = e.clipboardData) == null ? void 0 : _c2.setData("text/plain", tsvText);
5616
+ (_d2 = e.clipboardData) == null ? void 0 : _d2.setData("text/html", htmlText);
5420
5617
  };
5421
5618
  tableEl.onpaste = (e) => {
5422
- var _a3, _b3, _c, _d, _e, _f, _g, _h, _i;
5619
+ var _a3, _b3, _c2, _d2, _e2, _f2, _g2, _h2, _i;
5423
5620
  const anchor = sel.anchor;
5424
5621
  if (!anchor) return;
5425
5622
  const text = (_b3 = (_a3 = e.clipboardData) == null ? void 0 : _a3.getData("text/plain")) != null ? _b3 : "";
@@ -5430,15 +5627,15 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5430
5627
  if (editing) {
5431
5628
  editing.contentEditable = "inherit";
5432
5629
  const r = parseInt(
5433
- (_d = (_c = editing.closest("td")) == null ? void 0 : _c.getAttribute("data-row")) != null ? _d : "0",
5630
+ (_d2 = (_c2 = editing.closest("td")) == null ? void 0 : _c2.getAttribute("data-row")) != null ? _d2 : "0",
5434
5631
  10
5435
5632
  );
5436
5633
  const c = parseInt(
5437
- (_f = (_e = editing.closest("td")) == null ? void 0 : _e.getAttribute("data-col")) != null ? _f : "0",
5634
+ (_f2 = (_e2 = editing.closest("td")) == null ? void 0 : _e2.getAttribute("data-col")) != null ? _f2 : "0",
5438
5635
  10
5439
5636
  );
5440
5637
  if (cells[r]) {
5441
- cells[r][c] = (_g = editing.textContent) != null ? _g : "";
5638
+ cells[r][c] = (_g2 = editing.textContent) != null ? _g2 : "";
5442
5639
  }
5443
5640
  }
5444
5641
  if (!text.trim()) return;
@@ -5450,7 +5647,7 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
5450
5647
  const startC = anchor.col;
5451
5648
  const neededRows = startR + pasteRows.length;
5452
5649
  while (cells.length < neededRows) {
5453
- cells.push(Array((_i = (_h = cells[0]) == null ? void 0 : _h.length) != null ? _i : 1).fill(""));
5650
+ cells.push(Array((_i = (_h2 = cells[0]) == null ? void 0 : _h2.length) != null ? _i : 1).fill(""));
5454
5651
  }
5455
5652
  const maxPasteCols = Math.max(...pasteRows.map((r) => r.length));
5456
5653
  const neededCols = startC + maxPasteCols;
@@ -5739,10 +5936,50 @@ function defaultTableData(element) {
5739
5936
  function isTableData(v) {
5740
5937
  return v !== null && typeof v === "object" && "cells" in v && Array.isArray(v.cells);
5741
5938
  }
5939
+ function isTableDataWithFieldNames(v, cellsKey) {
5940
+ return v !== null && typeof v === "object" && cellsKey in v && Array.isArray(v[cellsKey]);
5941
+ }
5742
5942
  function renderTableElement(element, ctx, wrapper, pathKey) {
5943
+ var _a, _b, _c, _d;
5743
5944
  const state = ctx.state;
5744
5945
  const rawPrefill = ctx.prefill[element.key];
5745
- const initialData = isTableData(rawPrefill) ? rawPrefill : isTableData(element.default) ? element.default : defaultTableData(element);
5946
+ const cellsKey = (_b = (_a = element.fieldNames) == null ? void 0 : _a.cells) != null ? _b : "cells";
5947
+ const mergesKey = (_d = (_c = element.fieldNames) == null ? void 0 : _c.merges) != null ? _d : "merges";
5948
+ let initialData;
5949
+ if (isTableData(rawPrefill)) {
5950
+ initialData = {
5951
+ cells: rawPrefill.cells,
5952
+ merges: rawPrefill.merges ? migrateMerges(rawPrefill.merges) : []
5953
+ };
5954
+ } else if (rawPrefill && isTableDataWithFieldNames(rawPrefill, cellsKey)) {
5955
+ const rawMerges = rawPrefill[mergesKey];
5956
+ initialData = {
5957
+ cells: rawPrefill[cellsKey],
5958
+ merges: rawMerges ? migrateMerges(rawMerges) : []
5959
+ };
5960
+ } else if (isTableData(element.default)) {
5961
+ initialData = {
5962
+ cells: element.default.cells,
5963
+ merges: element.default.merges ? migrateMerges(element.default.merges) : []
5964
+ };
5965
+ } else if (element.default && isTableDataWithFieldNames(element.default, cellsKey)) {
5966
+ const rawMerges = element.default[mergesKey];
5967
+ initialData = {
5968
+ cells: element.default[cellsKey],
5969
+ merges: rawMerges ? migrateMerges(rawMerges) : []
5970
+ };
5971
+ } else {
5972
+ initialData = defaultTableData(element);
5973
+ }
5974
+ if (initialData.merges && initialData.merges.length > 0) {
5975
+ const rows = initialData.cells.length;
5976
+ const cols = rows > 0 ? initialData.cells[0].length : 0;
5977
+ const err = validateMerges(initialData.merges, rows, cols);
5978
+ if (err) {
5979
+ console.warn(`Table "${element.key}": invalid prefill merges stripped (${err})`);
5980
+ initialData = { ...initialData, merges: [] };
5981
+ }
5982
+ }
5746
5983
  if (state.config.readonly) {
5747
5984
  renderReadonlyTable(initialData, wrapper);
5748
5985
  } else {
@@ -5750,8 +5987,10 @@ function renderTableElement(element, ctx, wrapper, pathKey) {
5750
5987
  }
5751
5988
  }
5752
5989
  function validateTableElement(element, key, context) {
5990
+ var _a, _b;
5753
5991
  const { scopeRoot, skipValidation } = context;
5754
5992
  const errors = [];
5993
+ const cellsKey = (_b = (_a = element.fieldNames) == null ? void 0 : _a.cells) != null ? _b : "cells";
5755
5994
  const hiddenInput = scopeRoot.querySelector(
5756
5995
  `[name="${key}"]`
5757
5996
  );
@@ -5766,7 +6005,8 @@ function validateTableElement(element, key, context) {
5766
6005
  return { value: null, errors };
5767
6006
  }
5768
6007
  if (!skipValidation && element.required) {
5769
- const hasContent = value.cells.some(
6008
+ const cells = value[cellsKey];
6009
+ const hasContent = cells == null ? void 0 : cells.some(
5770
6010
  (row) => row.some((cell) => cell.trim() !== "")
5771
6011
  );
5772
6012
  if (!hasContent) {
@@ -5775,8 +6015,11 @@ function validateTableElement(element, key, context) {
5775
6015
  }
5776
6016
  return { value, errors };
5777
6017
  }
5778
- function updateTableField(_element, fieldPath, value, context) {
6018
+ function updateTableField(element, fieldPath, value, context) {
6019
+ var _a, _b, _c, _d;
5779
6020
  const { scopeRoot } = context;
6021
+ const cellsKey = (_b = (_a = element.fieldNames) == null ? void 0 : _a.cells) != null ? _b : "cells";
6022
+ const mergesKey = (_d = (_c = element.fieldNames) == null ? void 0 : _c.merges) != null ? _d : "merges";
5780
6023
  const hiddenInput = scopeRoot.querySelector(
5781
6024
  `[name="${fieldPath}"]`
5782
6025
  );
@@ -5786,13 +6029,1330 @@ function updateTableField(_element, fieldPath, value, context) {
5786
6029
  );
5787
6030
  return;
5788
6031
  }
5789
- if (isTableData(value) && hiddenInput._applyExternalUpdate) {
5790
- hiddenInput._applyExternalUpdate(value);
6032
+ let tableData = null;
6033
+ if (isTableData(value)) {
6034
+ tableData = {
6035
+ cells: value.cells,
6036
+ merges: value.merges ? migrateMerges(value.merges) : []
6037
+ };
6038
+ } else if (value && isTableDataWithFieldNames(value, cellsKey)) {
6039
+ const rawMerges = value[mergesKey];
6040
+ tableData = {
6041
+ cells: value[cellsKey],
6042
+ merges: rawMerges ? migrateMerges(rawMerges) : []
6043
+ };
6044
+ }
6045
+ if (tableData && hiddenInput._applyExternalUpdate) {
6046
+ hiddenInput._applyExternalUpdate(tableData);
5791
6047
  } else {
5792
6048
  hiddenInput.value = JSON.stringify(value);
5793
6049
  }
5794
6050
  }
5795
6051
 
6052
+ // src/components/richinput.ts
6053
+ function applyAutoExpand2(textarea, backdrop) {
6054
+ textarea.style.overflow = "hidden";
6055
+ textarea.style.resize = "none";
6056
+ const lineCount = (textarea.value.match(/\n/g) || []).length + 1;
6057
+ textarea.rows = Math.max(3, lineCount);
6058
+ const resize = () => {
6059
+ if (!textarea.isConnected) return;
6060
+ textarea.style.height = "0";
6061
+ textarea.style.height = `${textarea.scrollHeight}px`;
6062
+ if (backdrop) {
6063
+ backdrop.style.height = `${textarea.scrollHeight}px`;
6064
+ }
6065
+ };
6066
+ textarea.addEventListener("input", resize);
6067
+ setTimeout(() => {
6068
+ if (textarea.isConnected) resize();
6069
+ }, 0);
6070
+ }
6071
+ function buildFileLabels(files, state) {
6072
+ var _a, _b, _c, _d;
6073
+ const labels = /* @__PURE__ */ new Map();
6074
+ const nameCount = /* @__PURE__ */ new Map();
6075
+ for (const rid of files) {
6076
+ const meta = state.resourceIndex.get(rid);
6077
+ const name = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
6078
+ nameCount.set(name, ((_b = nameCount.get(name)) != null ? _b : 0) + 1);
6079
+ }
6080
+ for (const rid of files) {
6081
+ const meta = state.resourceIndex.get(rid);
6082
+ const name = (_c = meta == null ? void 0 : meta.name) != null ? _c : rid;
6083
+ if (((_d = nameCount.get(name)) != null ? _d : 1) > 1 && meta) {
6084
+ labels.set(rid, `${name} (${formatFileSize(meta.size)})`);
6085
+ } else {
6086
+ labels.set(rid, name);
6087
+ }
6088
+ }
6089
+ return labels;
6090
+ }
6091
+ function isImageMeta(meta) {
6092
+ if (!meta) return false;
6093
+ return meta.type.startsWith("image/");
6094
+ }
6095
+ function buildNameToRid(files, state) {
6096
+ const labels = buildFileLabels(files, state);
6097
+ const map = /* @__PURE__ */ new Map();
6098
+ for (const rid of files) {
6099
+ const label = labels.get(rid);
6100
+ if (label) map.set(label, rid);
6101
+ }
6102
+ return map;
6103
+ }
6104
+ function formatMention(name) {
6105
+ if (/\s/.test(name) || name.includes('"')) {
6106
+ return `@"${name.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
6107
+ }
6108
+ return `@${name}`;
6109
+ }
6110
+ function findAtTokens(text) {
6111
+ const tokens = [];
6112
+ const len = text.length;
6113
+ let i = 0;
6114
+ while (i < len) {
6115
+ if (text[i] === "@") {
6116
+ if (i > 0 && !/\s/.test(text[i - 1])) {
6117
+ i++;
6118
+ continue;
6119
+ }
6120
+ const start = i;
6121
+ i++;
6122
+ if (i < len && text[i] === '"') {
6123
+ i++;
6124
+ let name = "";
6125
+ while (i < len && text[i] !== '"') {
6126
+ if (text[i] === "\\" && i + 1 < len) {
6127
+ name += text[i + 1];
6128
+ i += 2;
6129
+ } else {
6130
+ name += text[i];
6131
+ i++;
6132
+ }
6133
+ }
6134
+ if (i < len && text[i] === '"') {
6135
+ i++;
6136
+ if (i >= len || /\s/.test(text[i])) {
6137
+ tokens.push({ start, end: i, raw: text.slice(start, i), name });
6138
+ }
6139
+ }
6140
+ } else if (i < len && !/\s/.test(text[i])) {
6141
+ const wordStart = i;
6142
+ while (i < len && !/\s/.test(text[i])) {
6143
+ i++;
6144
+ }
6145
+ const name = text.slice(wordStart, i);
6146
+ tokens.push({ start, end: i, raw: text.slice(start, i), name });
6147
+ }
6148
+ } else {
6149
+ i++;
6150
+ }
6151
+ }
6152
+ return tokens;
6153
+ }
6154
+ function findMentions(text, nameToRid) {
6155
+ if (nameToRid.size === 0) return [];
6156
+ const tokens = findAtTokens(text);
6157
+ const results = [];
6158
+ for (const token of tokens) {
6159
+ const rid = nameToRid.get(token.name);
6160
+ if (rid) {
6161
+ results.push({
6162
+ start: token.start,
6163
+ end: token.end,
6164
+ name: token.name,
6165
+ rid
6166
+ });
6167
+ }
6168
+ }
6169
+ return results;
6170
+ }
6171
+ function replaceFilenamesWithRids(text, nameToRid) {
6172
+ const mentions = findMentions(text, nameToRid);
6173
+ if (mentions.length === 0) return text;
6174
+ let result = "";
6175
+ let lastIdx = 0;
6176
+ for (const m of mentions) {
6177
+ result += text.slice(lastIdx, m.start);
6178
+ result += `@${m.rid}`;
6179
+ lastIdx = m.end;
6180
+ }
6181
+ result += text.slice(lastIdx);
6182
+ return result;
6183
+ }
6184
+ function replaceRidsWithFilenames(text, files, state) {
6185
+ const labels = buildFileLabels(files, state);
6186
+ const ridToLabel = /* @__PURE__ */ new Map();
6187
+ for (const rid of files) {
6188
+ const label = labels.get(rid);
6189
+ if (label) ridToLabel.set(rid, label);
6190
+ }
6191
+ const tokens = findAtTokens(text);
6192
+ if (tokens.length === 0) return text;
6193
+ let result = "";
6194
+ let lastIdx = 0;
6195
+ for (const token of tokens) {
6196
+ result += text.slice(lastIdx, token.start);
6197
+ const label = ridToLabel.get(token.name);
6198
+ if (label) {
6199
+ result += formatMention(label);
6200
+ } else {
6201
+ result += token.raw;
6202
+ }
6203
+ lastIdx = token.end;
6204
+ }
6205
+ result += text.slice(lastIdx);
6206
+ return result;
6207
+ }
6208
+ function renderThumbContent(thumb, rid, meta, state) {
6209
+ clear(thumb);
6210
+ if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
6211
+ const img = document.createElement("img");
6212
+ img.alt = meta.name;
6213
+ img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
6214
+ const reader = new FileReader();
6215
+ reader.onload = (e) => {
6216
+ var _a;
6217
+ img.src = ((_a = e.target) == null ? void 0 : _a.result) || "";
6218
+ };
6219
+ reader.readAsDataURL(meta.file);
6220
+ thumb.appendChild(img);
6221
+ } else if (state.config.getThumbnail) {
6222
+ state.config.getThumbnail(rid).then((url) => {
6223
+ var _a;
6224
+ if (!url || !thumb.isConnected) return;
6225
+ const img = document.createElement("img");
6226
+ img.alt = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
6227
+ img.src = url;
6228
+ img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
6229
+ clear(thumb);
6230
+ thumb.appendChild(img);
6231
+ }).catch((err) => {
6232
+ var _a, _b;
6233
+ (_b = (_a = state.config).onThumbnailError) == null ? void 0 : _b.call(_a, err, rid);
6234
+ });
6235
+ const placeholder = document.createElement("div");
6236
+ placeholder.style.cssText = "width: 100%; height: 100%; background: var(--fb-background-hover-color, #f3f4f6); display: flex; align-items: center; justify-content: center;";
6237
+ placeholder.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>';
6238
+ thumb.appendChild(placeholder);
6239
+ } else {
6240
+ const icon = document.createElement("div");
6241
+ icon.style.cssText = "width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 2px; box-sizing: border-box;";
6242
+ icon.innerHTML = '<svg width="20" height="20" 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"/><polyline points="14 2 14 8 20 8"/></svg>';
6243
+ if (meta == null ? void 0 : meta.name) {
6244
+ const nameEl = document.createElement("span");
6245
+ nameEl.style.cssText = "font-size: 9px; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 44px; color: var(--fb-text-color, #111827);";
6246
+ nameEl.textContent = meta.name;
6247
+ icon.appendChild(nameEl);
6248
+ }
6249
+ thumb.appendChild(icon);
6250
+ }
6251
+ }
6252
+ function renderImagePreview(hoverEl, rid, meta, state) {
6253
+ clear(hoverEl);
6254
+ if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
6255
+ const img = document.createElement("img");
6256
+ img.alt = meta.name;
6257
+ img.style.cssText = "max-width: 120px; max-height: 120px; object-fit: contain; display: block;";
6258
+ const reader = new FileReader();
6259
+ reader.onload = (e) => {
6260
+ var _a;
6261
+ img.src = ((_a = e.target) == null ? void 0 : _a.result) || "";
6262
+ };
6263
+ reader.readAsDataURL(meta.file);
6264
+ hoverEl.appendChild(img);
6265
+ } else if (state.config.getThumbnail) {
6266
+ state.config.getThumbnail(rid).then((url) => {
6267
+ var _a;
6268
+ if (!url || !hoverEl.isConnected) return;
6269
+ const img = document.createElement("img");
6270
+ img.alt = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
6271
+ img.src = url;
6272
+ img.style.cssText = "max-width: 120px; max-height: 120px; object-fit: contain; display: block;";
6273
+ clear(hoverEl);
6274
+ hoverEl.appendChild(img);
6275
+ }).catch((err) => {
6276
+ var _a, _b;
6277
+ (_b = (_a = state.config).onThumbnailError) == null ? void 0 : _b.call(_a, err, rid);
6278
+ });
6279
+ }
6280
+ }
6281
+ function positionPortalTooltip(tooltip, anchor) {
6282
+ const rect = anchor.getBoundingClientRect();
6283
+ const ttRect = tooltip.getBoundingClientRect();
6284
+ const left = Math.max(
6285
+ 4,
6286
+ Math.min(
6287
+ rect.left + rect.width / 2 - ttRect.width / 2,
6288
+ window.innerWidth - ttRect.width - 4
6289
+ )
6290
+ );
6291
+ const topAbove = rect.top - ttRect.height - 8;
6292
+ const topBelow = rect.bottom + 8;
6293
+ const top = topAbove >= 4 ? topAbove : topBelow;
6294
+ tooltip.style.left = `${left}px`;
6295
+ tooltip.style.top = `${Math.max(4, top)}px`;
6296
+ }
6297
+ function showMentionTooltip(anchor, rid, state) {
6298
+ const meta = state.resourceIndex.get(rid);
6299
+ const tooltip = document.createElement("div");
6300
+ tooltip.className = "fb-richinput-portal-tooltip fb-richinput-mention-tooltip";
6301
+ tooltip.style.cssText = `
6302
+ position: fixed;
6303
+ z-index: 99999;
6304
+ background: #fff;
6305
+ border: 1px solid var(--fb-border-color, #d1d5db);
6306
+ border-radius: 8px;
6307
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
6308
+ padding: 4px;
6309
+ pointer-events: none;
6310
+ min-width: 60px;
6311
+ max-width: 140px;
6312
+ `;
6313
+ const preview = document.createElement("div");
6314
+ preview.style.cssText = "min-height: 60px; max-height: 120px; display: flex; align-items: center; justify-content: center;";
6315
+ renderImagePreview(preview, rid, meta, state);
6316
+ tooltip.appendChild(preview);
6317
+ document.body.appendChild(tooltip);
6318
+ positionPortalTooltip(tooltip, anchor);
6319
+ return tooltip;
6320
+ }
6321
+ function showFileTooltip(anchor, opts) {
6322
+ var _a, _b;
6323
+ const { rid, state, isReadonly, onMention, onRemove } = opts;
6324
+ const meta = state.resourceIndex.get(rid);
6325
+ const filename = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
6326
+ const tooltip = document.createElement("div");
6327
+ tooltip.className = "fb-richinput-portal-tooltip fb-richinput-file-tooltip";
6328
+ tooltip.style.cssText = `
6329
+ position: fixed;
6330
+ z-index: 99999;
6331
+ background: #fff;
6332
+ border: 1px solid var(--fb-border-color, #d1d5db);
6333
+ border-radius: 8px;
6334
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
6335
+ padding: 4px;
6336
+ pointer-events: auto;
6337
+ min-width: 80px;
6338
+ max-width: 260px;
6339
+ `;
6340
+ const preview = document.createElement("div");
6341
+ preview.style.cssText = "min-height: 60px; max-height: 120px; display: flex; align-items: center; justify-content: center;";
6342
+ renderImagePreview(preview, rid, meta, state);
6343
+ tooltip.appendChild(preview);
6344
+ const nameEl = document.createElement("div");
6345
+ nameEl.style.cssText = "padding: 4px 6px 2px; font-size: 12px; color: var(--fb-text-color, #111827); word-break: break-word; border-top: 1px solid var(--fb-border-color, #d1d5db);";
6346
+ nameEl.textContent = filename;
6347
+ tooltip.appendChild(nameEl);
6348
+ const actionsRow = document.createElement("div");
6349
+ actionsRow.style.cssText = "display: flex; align-items: center; gap: 2px; padding: 3px 4px 2px; border-top: 1px solid var(--fb-border-color, #d1d5db); justify-content: center;";
6350
+ const btnStyle = "background: none; border: none; cursor: pointer; padding: 3px 5px; border-radius: 4px; color: var(--fb-text-muted-color, #6b7280); display: flex; align-items: center; transition: background 0.1s, color 0.1s;";
6351
+ const btnHoverIn = (btn) => {
6352
+ btn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
6353
+ btn.style.color = "var(--fb-text-color, #111827)";
6354
+ };
6355
+ const btnHoverOut = (btn) => {
6356
+ btn.style.background = "none";
6357
+ btn.style.color = "var(--fb-text-muted-color, #6b7280)";
6358
+ };
6359
+ if (!isReadonly && onMention) {
6360
+ const mentionBtn = document.createElement("button");
6361
+ mentionBtn.type = "button";
6362
+ mentionBtn.title = "Mention";
6363
+ mentionBtn.style.cssText = btnStyle;
6364
+ mentionBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"/></svg>';
6365
+ mentionBtn.addEventListener("mouseenter", () => btnHoverIn(mentionBtn));
6366
+ mentionBtn.addEventListener("mouseleave", () => btnHoverOut(mentionBtn));
6367
+ mentionBtn.addEventListener("click", (e) => {
6368
+ e.stopPropagation();
6369
+ onMention();
6370
+ tooltip.remove();
6371
+ });
6372
+ actionsRow.appendChild(mentionBtn);
6373
+ }
6374
+ if (state.config.downloadFile) {
6375
+ const dlBtn = document.createElement("button");
6376
+ dlBtn.type = "button";
6377
+ dlBtn.title = "Download";
6378
+ dlBtn.style.cssText = btnStyle;
6379
+ dlBtn.innerHTML = '<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="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>';
6380
+ dlBtn.addEventListener("mouseenter", () => btnHoverIn(dlBtn));
6381
+ dlBtn.addEventListener("mouseleave", () => btnHoverOut(dlBtn));
6382
+ dlBtn.addEventListener("click", (e) => {
6383
+ var _a2, _b2;
6384
+ e.stopPropagation();
6385
+ (_b2 = (_a2 = state.config).downloadFile) == null ? void 0 : _b2.call(_a2, rid, filename);
6386
+ });
6387
+ actionsRow.appendChild(dlBtn);
6388
+ }
6389
+ const hasOpenUrl = !!((_b = state.config.getDownloadUrl) != null ? _b : state.config.getThumbnail);
6390
+ if (hasOpenUrl) {
6391
+ const openBtn = document.createElement("button");
6392
+ openBtn.type = "button";
6393
+ openBtn.title = "Open in new window";
6394
+ openBtn.style.cssText = btnStyle;
6395
+ openBtn.innerHTML = '<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 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>';
6396
+ openBtn.addEventListener("mouseenter", () => btnHoverIn(openBtn));
6397
+ openBtn.addEventListener("mouseleave", () => btnHoverOut(openBtn));
6398
+ openBtn.addEventListener("click", (e) => {
6399
+ var _a2, _b2, _c, _d;
6400
+ e.stopPropagation();
6401
+ if (state.config.getDownloadUrl) {
6402
+ const url = state.config.getDownloadUrl(rid);
6403
+ if (url) {
6404
+ window.open(url, "_blank");
6405
+ } else {
6406
+ (_b2 = (_a2 = state.config).getThumbnail) == null ? void 0 : _b2.call(_a2, rid).then((thumbUrl) => {
6407
+ if (thumbUrl) window.open(thumbUrl, "_blank");
6408
+ }).catch(() => {
6409
+ });
6410
+ }
6411
+ } else {
6412
+ (_d = (_c = state.config).getThumbnail) == null ? void 0 : _d.call(_c, rid).then((url) => {
6413
+ if (url) window.open(url, "_blank");
6414
+ }).catch(() => {
6415
+ });
6416
+ }
6417
+ });
6418
+ actionsRow.appendChild(openBtn);
6419
+ }
6420
+ if (!isReadonly && onRemove) {
6421
+ const removeBtn = document.createElement("button");
6422
+ removeBtn.type = "button";
6423
+ removeBtn.title = "Remove";
6424
+ removeBtn.style.cssText = btnStyle;
6425
+ removeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>';
6426
+ removeBtn.addEventListener("mouseenter", () => {
6427
+ removeBtn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
6428
+ removeBtn.style.color = "var(--fb-error-color, #ef4444)";
6429
+ });
6430
+ removeBtn.addEventListener("mouseleave", () => btnHoverOut(removeBtn));
6431
+ removeBtn.addEventListener("click", (e) => {
6432
+ e.stopPropagation();
6433
+ tooltip.remove();
6434
+ onRemove();
6435
+ });
6436
+ actionsRow.appendChild(removeBtn);
6437
+ }
6438
+ const hasActions = actionsRow.children.length > 0;
6439
+ if (hasActions) {
6440
+ tooltip.appendChild(actionsRow);
6441
+ }
6442
+ document.body.appendChild(tooltip);
6443
+ positionPortalTooltip(tooltip, anchor);
6444
+ return tooltip;
6445
+ }
6446
+ function createTooltipHandle() {
6447
+ return { element: null, hideTimer: null };
6448
+ }
6449
+ function scheduleHideTooltip(handle, delayMs = 150) {
6450
+ if (handle.hideTimer !== null) return;
6451
+ handle.hideTimer = setTimeout(() => {
6452
+ handle.hideTimer = null;
6453
+ if (handle.element) {
6454
+ handle.element.remove();
6455
+ handle.element = null;
6456
+ }
6457
+ }, delayMs);
6458
+ }
6459
+ function cancelHideTooltip(handle) {
6460
+ if (handle.hideTimer !== null) {
6461
+ clearTimeout(handle.hideTimer);
6462
+ handle.hideTimer = null;
6463
+ }
6464
+ }
6465
+ function removePortalTooltip(tooltip) {
6466
+ if (tooltip) tooltip.remove();
6467
+ return null;
6468
+ }
6469
+ function getAtTrigger(textarea) {
6470
+ var _a;
6471
+ const cursorPos = (_a = textarea.selectionStart) != null ? _a : 0;
6472
+ const textBefore = textarea.value.slice(0, cursorPos);
6473
+ for (let i = textBefore.length - 1; i >= 0; i--) {
6474
+ if (textBefore[i] === "@") {
6475
+ if (i === 0 || /\s/.test(textBefore[i - 1])) {
6476
+ let query = textBefore.slice(i + 1);
6477
+ if (query.startsWith('"')) {
6478
+ query = query.slice(1);
6479
+ }
6480
+ return { query, pos: i };
6481
+ }
6482
+ return null;
6483
+ }
6484
+ }
6485
+ return null;
6486
+ }
6487
+ function filterFilesForDropdown(query, files, labels) {
6488
+ const lq = query.toLowerCase();
6489
+ return files.filter((rid) => {
6490
+ var _a;
6491
+ const label = (_a = labels.get(rid)) != null ? _a : rid;
6492
+ return label.toLowerCase().includes(lq);
6493
+ });
6494
+ }
6495
+ var TEXTAREA_FONT = "font-size: var(--fb-font-size, 14px); font-family: var(--fb-font-family, inherit); line-height: 1.6;";
6496
+ var TEXTAREA_PADDING = "padding: 12px 52px 12px 14px;";
6497
+ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
6498
+ var _a;
6499
+ const state = ctx.state;
6500
+ const files = [...initialValue.files];
6501
+ const dropdownState = {
6502
+ open: false,
6503
+ query: "",
6504
+ triggerPos: -1,
6505
+ selectedIndex: 0
6506
+ };
6507
+ const docListenerCtrl = new AbortController();
6508
+ const hiddenInput = document.createElement("input");
6509
+ hiddenInput.type = "hidden";
6510
+ hiddenInput.name = pathKey;
6511
+ function getCurrentValue() {
6512
+ var _a2, _b;
6513
+ const rawText = textarea.value;
6514
+ const nameToRid = buildNameToRid(files, state);
6515
+ const submissionText = rawText ? replaceFilenamesWithRids(rawText, nameToRid) : null;
6516
+ const textKey = (_a2 = element.textKey) != null ? _a2 : "text";
6517
+ const filesKey = (_b = element.filesKey) != null ? _b : "files";
6518
+ return {
6519
+ [textKey]: rawText === "" ? null : submissionText,
6520
+ [filesKey]: [...files]
6521
+ };
6522
+ }
6523
+ function writeHidden() {
6524
+ hiddenInput.value = JSON.stringify(getCurrentValue());
6525
+ }
6526
+ const outerDiv = document.createElement("div");
6527
+ outerDiv.className = "fb-richinput-wrapper";
6528
+ outerDiv.style.cssText = `
6529
+ position: relative;
6530
+ border: 1px solid var(--fb-border-color, #d1d5db);
6531
+ border-radius: 16px;
6532
+ background: var(--fb-background-color, #f9fafb);
6533
+ transition: box-shadow 0.15s, border-color 0.15s;
6534
+ `;
6535
+ outerDiv.addEventListener("focusin", () => {
6536
+ outerDiv.style.borderColor = "var(--fb-primary-color, #0066cc)";
6537
+ outerDiv.style.boxShadow = "0 0 0 2px color-mix(in srgb, var(--fb-primary-color, #0066cc) 25%, transparent)";
6538
+ });
6539
+ outerDiv.addEventListener("focusout", () => {
6540
+ outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
6541
+ outerDiv.style.boxShadow = "none";
6542
+ });
6543
+ let dragCounter = 0;
6544
+ outerDiv.addEventListener("dragenter", (e) => {
6545
+ e.preventDefault();
6546
+ dragCounter++;
6547
+ outerDiv.style.borderColor = "var(--fb-primary-color, #0066cc)";
6548
+ outerDiv.style.boxShadow = "0 0 0 2px color-mix(in srgb, var(--fb-primary-color, #0066cc) 25%, transparent)";
6549
+ });
6550
+ outerDiv.addEventListener("dragover", (e) => {
6551
+ e.preventDefault();
6552
+ });
6553
+ outerDiv.addEventListener("dragleave", (e) => {
6554
+ e.preventDefault();
6555
+ dragCounter--;
6556
+ if (dragCounter <= 0) {
6557
+ dragCounter = 0;
6558
+ outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
6559
+ outerDiv.style.boxShadow = "none";
6560
+ }
6561
+ });
6562
+ outerDiv.addEventListener("drop", (e) => {
6563
+ var _a2, _b;
6564
+ e.preventDefault();
6565
+ dragCounter = 0;
6566
+ outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
6567
+ outerDiv.style.boxShadow = "none";
6568
+ const droppedFiles = (_a2 = e.dataTransfer) == null ? void 0 : _a2.files;
6569
+ if (!droppedFiles || !state.config.uploadFile) return;
6570
+ const maxFiles = (_b = element.maxFiles) != null ? _b : Infinity;
6571
+ for (let i = 0; i < droppedFiles.length && files.length < maxFiles; i++) {
6572
+ uploadFile(droppedFiles[i]);
6573
+ }
6574
+ });
6575
+ const filesRow = document.createElement("div");
6576
+ filesRow.className = "fb-richinput-files";
6577
+ filesRow.style.cssText = "display: none; flex-wrap: wrap; gap: 6px; padding: 10px 14px 0; align-items: center;";
6578
+ const fileInput = document.createElement("input");
6579
+ fileInput.type = "file";
6580
+ fileInput.multiple = true;
6581
+ fileInput.style.display = "none";
6582
+ if (element.accept) {
6583
+ if (typeof element.accept === "string") {
6584
+ fileInput.accept = element.accept;
6585
+ } else {
6586
+ fileInput.accept = element.accept.extensions.map((ext) => ext.startsWith(".") ? ext : `.${ext}`).join(",");
6587
+ }
6588
+ }
6589
+ const textareaArea = document.createElement("div");
6590
+ textareaArea.style.cssText = "position: relative;";
6591
+ const backdrop = document.createElement("div");
6592
+ backdrop.className = "fb-richinput-backdrop";
6593
+ backdrop.style.cssText = `
6594
+ position: absolute;
6595
+ top: 0; left: 0; right: 0; bottom: 0;
6596
+ ${TEXTAREA_PADDING}
6597
+ ${TEXTAREA_FONT}
6598
+ white-space: pre-wrap;
6599
+ word-break: break-word;
6600
+ color: transparent;
6601
+ pointer-events: none;
6602
+ overflow: hidden;
6603
+ border-radius: inherit;
6604
+ box-sizing: border-box;
6605
+ z-index: 2;
6606
+ `;
6607
+ const textarea = document.createElement("textarea");
6608
+ textarea.name = `${pathKey}__text`;
6609
+ textarea.placeholder = element.placeholder || t("richinputPlaceholder", state);
6610
+ const rawInitialText = (_a = initialValue.text) != null ? _a : "";
6611
+ textarea.value = rawInitialText ? replaceRidsWithFilenames(rawInitialText, files, state) : "";
6612
+ textarea.style.cssText = `
6613
+ width: 100%;
6614
+ ${TEXTAREA_PADDING}
6615
+ ${TEXTAREA_FONT}
6616
+ background: transparent;
6617
+ border: none;
6618
+ outline: none;
6619
+ resize: none;
6620
+ color: var(--fb-text-color, #111827);
6621
+ box-sizing: border-box;
6622
+ position: relative;
6623
+ z-index: 1;
6624
+ caret-color: var(--fb-text-color, #111827);
6625
+ `;
6626
+ applyAutoExpand2(textarea, backdrop);
6627
+ textarea.addEventListener("scroll", () => {
6628
+ backdrop.scrollTop = textarea.scrollTop;
6629
+ });
6630
+ let mentionTooltip = null;
6631
+ backdrop.addEventListener("mouseover", (e) => {
6632
+ var _a2, _b;
6633
+ const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
6634
+ if (!(mark == null ? void 0 : mark.dataset.rid)) return;
6635
+ mentionTooltip = removePortalTooltip(mentionTooltip);
6636
+ mentionTooltip = showMentionTooltip(mark, mark.dataset.rid, state);
6637
+ });
6638
+ backdrop.addEventListener("mouseout", (e) => {
6639
+ var _a2, _b, _c;
6640
+ const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
6641
+ if (!mark) return;
6642
+ const related = e.relatedTarget;
6643
+ if ((_c = related == null ? void 0 : related.closest) == null ? void 0 : _c.call(related, "mark")) return;
6644
+ mentionTooltip = removePortalTooltip(mentionTooltip);
6645
+ });
6646
+ backdrop.addEventListener("mousedown", (e) => {
6647
+ var _a2, _b;
6648
+ const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
6649
+ if (!mark) return;
6650
+ mentionTooltip = removePortalTooltip(mentionTooltip);
6651
+ const marks = backdrop.querySelectorAll("mark");
6652
+ marks.forEach((m) => m.style.pointerEvents = "none");
6653
+ const under = document.elementFromPoint(e.clientX, e.clientY);
6654
+ if (under) {
6655
+ under.dispatchEvent(
6656
+ new MouseEvent("mousedown", {
6657
+ bubbles: true,
6658
+ cancelable: true,
6659
+ view: window,
6660
+ clientX: e.clientX,
6661
+ clientY: e.clientY,
6662
+ button: e.button,
6663
+ buttons: e.buttons,
6664
+ detail: e.detail
6665
+ })
6666
+ );
6667
+ }
6668
+ document.addEventListener(
6669
+ "mouseup",
6670
+ () => {
6671
+ marks.forEach((m) => m.style.pointerEvents = "auto");
6672
+ },
6673
+ { once: true }
6674
+ );
6675
+ });
6676
+ function updateBackdrop() {
6677
+ const text = textarea.value;
6678
+ const nameToRid = buildNameToRid(files, state);
6679
+ const tokens = findAtTokens(text);
6680
+ if (tokens.length === 0) {
6681
+ backdrop.innerHTML = escapeHtml(text) + "\n";
6682
+ return;
6683
+ }
6684
+ let html = "";
6685
+ let lastIdx = 0;
6686
+ for (const token of tokens) {
6687
+ html += escapeHtml(text.slice(lastIdx, token.start));
6688
+ const rid = nameToRid.get(token.name);
6689
+ if (rid) {
6690
+ html += `<mark data-rid="${escapeHtml(rid)}" style="background: color-mix(in srgb, var(--fb-primary-color, #0066cc) 15%, transparent); color: transparent; border-radius: 8px; padding: 0; border: none; box-shadow: 0 0 0 2px color-mix(in srgb, var(--fb-primary-color, #0066cc) 15%, transparent), 0 0 0 3px color-mix(in srgb, var(--fb-primary-color, #0066cc) 30%, transparent); box-decoration-break: clone; -webkit-box-decoration-break: clone; pointer-events: auto; cursor: text;">${escapeHtml(text.slice(token.start, token.end))}</mark>`;
6691
+ } else {
6692
+ html += `<mark style="color: transparent; background: none; padding: 0; border: none; text-decoration-line: underline; text-decoration-style: wavy; text-decoration-color: rgba(239, 68, 68, 0.45); text-underline-offset: 2px;">${escapeHtml(text.slice(token.start, token.end))}</mark>`;
6693
+ }
6694
+ lastIdx = token.end;
6695
+ }
6696
+ html += escapeHtml(text.slice(lastIdx));
6697
+ backdrop.innerHTML = html + "\n";
6698
+ }
6699
+ const paperclipBtn = document.createElement("button");
6700
+ paperclipBtn.type = "button";
6701
+ paperclipBtn.title = t("richinputAttachFile", state);
6702
+ paperclipBtn.style.cssText = `
6703
+ position: absolute;
6704
+ right: 10px;
6705
+ bottom: 10px;
6706
+ z-index: 2;
6707
+ width: 32px;
6708
+ height: 32px;
6709
+ border: none;
6710
+ border-radius: 8px;
6711
+ background: transparent;
6712
+ cursor: pointer;
6713
+ display: flex;
6714
+ align-items: center;
6715
+ justify-content: center;
6716
+ color: var(--fb-text-muted-color, #9ca3af);
6717
+ transition: color 0.15s, background 0.15s;
6718
+ `;
6719
+ paperclipBtn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>';
6720
+ paperclipBtn.addEventListener("mouseenter", () => {
6721
+ paperclipBtn.style.color = "var(--fb-primary-color, #0066cc)";
6722
+ paperclipBtn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
6723
+ });
6724
+ paperclipBtn.addEventListener("mouseleave", () => {
6725
+ paperclipBtn.style.color = "var(--fb-text-muted-color, #9ca3af)";
6726
+ paperclipBtn.style.background = "transparent";
6727
+ });
6728
+ paperclipBtn.addEventListener("click", () => {
6729
+ var _a2;
6730
+ const maxFiles = (_a2 = element.maxFiles) != null ? _a2 : Infinity;
6731
+ if (files.length < maxFiles) {
6732
+ fileInput.click();
6733
+ }
6734
+ });
6735
+ const dropdown = document.createElement("div");
6736
+ dropdown.className = "fb-richinput-dropdown";
6737
+ dropdown.style.cssText = `
6738
+ display: none;
6739
+ position: absolute;
6740
+ bottom: 100%;
6741
+ left: 0;
6742
+ z-index: 1000;
6743
+ background: #fff;
6744
+ border: 1px solid var(--fb-border-color, #d1d5db);
6745
+ border-radius: var(--fb-border-radius, 6px);
6746
+ box-shadow: 0 4px 12px rgba(0,0,0,0.12);
6747
+ min-width: 180px;
6748
+ max-width: 320px;
6749
+ max-height: 200px;
6750
+ overflow-y: auto;
6751
+ margin-bottom: 4px;
6752
+ ${TEXTAREA_FONT}
6753
+ `;
6754
+ function buildFileLabelsFromClosure() {
6755
+ return buildFileLabels(files, state);
6756
+ }
6757
+ function renderDropdownItems(filtered) {
6758
+ clear(dropdown);
6759
+ const labels = buildFileLabelsFromClosure();
6760
+ if (filtered.length === 0) {
6761
+ dropdown.style.display = "none";
6762
+ dropdownState.open = false;
6763
+ return;
6764
+ }
6765
+ filtered.forEach((rid, idx) => {
6766
+ var _a2;
6767
+ const meta = state.resourceIndex.get(rid);
6768
+ const item = document.createElement("div");
6769
+ item.className = "fb-richinput-dropdown-item";
6770
+ item.dataset.rid = rid;
6771
+ item.style.cssText = `
6772
+ padding: 5px 10px;
6773
+ cursor: pointer;
6774
+ color: var(--fb-text-color, #111827);
6775
+ background: ${idx === dropdownState.selectedIndex ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent"};
6776
+ display: flex;
6777
+ align-items: center;
6778
+ gap: 8px;
6779
+ `;
6780
+ const thumb = document.createElement("div");
6781
+ thumb.style.cssText = "width: 24px; height: 24px; border-radius: 4px; overflow: hidden; flex-shrink: 0; background: var(--fb-background-hover-color, #f3f4f6); display: flex; align-items: center; justify-content: center;";
6782
+ if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
6783
+ const img = document.createElement("img");
6784
+ img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
6785
+ const reader = new FileReader();
6786
+ reader.onload = (ev) => {
6787
+ var _a3;
6788
+ img.src = ((_a3 = ev.target) == null ? void 0 : _a3.result) || "";
6789
+ };
6790
+ reader.readAsDataURL(meta.file);
6791
+ thumb.appendChild(img);
6792
+ } else if (state.config.getThumbnail) {
6793
+ state.config.getThumbnail(rid).then((url) => {
6794
+ if (!url || !thumb.isConnected) return;
6795
+ const img = document.createElement("img");
6796
+ img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
6797
+ img.src = url;
6798
+ clear(thumb);
6799
+ thumb.appendChild(img);
6800
+ }).catch(() => {
6801
+ });
6802
+ thumb.innerHTML = '<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"/><polyline points="14 2 14 8 20 8"/></svg>';
6803
+ } else {
6804
+ thumb.innerHTML = '<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"/><polyline points="14 2 14 8 20 8"/></svg>';
6805
+ }
6806
+ item.appendChild(thumb);
6807
+ const nameSpan = document.createElement("span");
6808
+ nameSpan.style.cssText = "overflow: hidden; text-overflow: ellipsis; white-space: nowrap;";
6809
+ nameSpan.textContent = (_a2 = labels.get(rid)) != null ? _a2 : rid;
6810
+ item.appendChild(nameSpan);
6811
+ dropdown.appendChild(item);
6812
+ });
6813
+ dropdown.onmousemove = (e) => {
6814
+ var _a2, _b, _c;
6815
+ const target = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(
6816
+ _a2,
6817
+ ".fb-richinput-dropdown-item"
6818
+ );
6819
+ if (!target) return;
6820
+ const newIdx = filtered.indexOf((_c = target.dataset.rid) != null ? _c : "");
6821
+ if (newIdx === -1 || newIdx === dropdownState.selectedIndex) return;
6822
+ const items = dropdown.querySelectorAll(
6823
+ ".fb-richinput-dropdown-item"
6824
+ );
6825
+ items.forEach((el, i) => {
6826
+ el.style.background = i === newIdx ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent";
6827
+ });
6828
+ dropdownState.selectedIndex = newIdx;
6829
+ };
6830
+ dropdown.onmousedown = (e) => {
6831
+ var _a2, _b;
6832
+ e.preventDefault();
6833
+ e.stopPropagation();
6834
+ const target = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(
6835
+ _a2,
6836
+ ".fb-richinput-dropdown-item"
6837
+ );
6838
+ if (!(target == null ? void 0 : target.dataset.rid)) return;
6839
+ insertMention(target.dataset.rid);
6840
+ };
6841
+ dropdown.style.display = "block";
6842
+ dropdownState.open = true;
6843
+ }
6844
+ function openDropdown() {
6845
+ const trigger = getAtTrigger(textarea);
6846
+ if (!trigger) {
6847
+ closeDropdown();
6848
+ return;
6849
+ }
6850
+ dropdownState.query = trigger.query;
6851
+ dropdownState.triggerPos = trigger.pos;
6852
+ dropdownState.selectedIndex = 0;
6853
+ const labels = buildFileLabelsFromClosure();
6854
+ const filtered = filterFilesForDropdown(trigger.query, files, labels);
6855
+ renderDropdownItems(filtered);
6856
+ }
6857
+ function closeDropdown() {
6858
+ dropdown.style.display = "none";
6859
+ dropdownState.open = false;
6860
+ }
6861
+ function insertMention(rid) {
6862
+ var _a2, _b, _c, _d;
6863
+ const labels = buildFileLabelsFromClosure();
6864
+ const label = (_c = (_b = labels.get(rid)) != null ? _b : (_a2 = state.resourceIndex.get(rid)) == null ? void 0 : _a2.name) != null ? _c : rid;
6865
+ const cursorPos = (_d = textarea.selectionStart) != null ? _d : 0;
6866
+ const before = textarea.value.slice(0, dropdownState.triggerPos);
6867
+ const after = textarea.value.slice(cursorPos);
6868
+ const mention = `${formatMention(label)} `;
6869
+ textarea.value = `${before}${mention}${after}`;
6870
+ const newPos = before.length + mention.length;
6871
+ textarea.setSelectionRange(newPos, newPos);
6872
+ textarea.dispatchEvent(new Event("input"));
6873
+ closeDropdown();
6874
+ }
6875
+ textarea.addEventListener("input", () => {
6876
+ var _a2;
6877
+ updateBackdrop();
6878
+ writeHidden();
6879
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
6880
+ if (files.length > 0) {
6881
+ openDropdown();
6882
+ } else {
6883
+ closeDropdown();
6884
+ }
6885
+ });
6886
+ function updateDropdownHighlight() {
6887
+ const items = dropdown.querySelectorAll(
6888
+ ".fb-richinput-dropdown-item"
6889
+ );
6890
+ items.forEach((el, i) => {
6891
+ el.style.background = i === dropdownState.selectedIndex ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent";
6892
+ });
6893
+ }
6894
+ textarea.addEventListener("keydown", (e) => {
6895
+ if (!dropdownState.open) return;
6896
+ const labels = buildFileLabelsFromClosure();
6897
+ const filtered = filterFilesForDropdown(
6898
+ dropdownState.query,
6899
+ files,
6900
+ labels
6901
+ );
6902
+ if (e.key === "ArrowDown") {
6903
+ e.preventDefault();
6904
+ dropdownState.selectedIndex = Math.min(
6905
+ dropdownState.selectedIndex + 1,
6906
+ filtered.length - 1
6907
+ );
6908
+ updateDropdownHighlight();
6909
+ } else if (e.key === "ArrowUp") {
6910
+ e.preventDefault();
6911
+ dropdownState.selectedIndex = Math.max(
6912
+ dropdownState.selectedIndex - 1,
6913
+ 0
6914
+ );
6915
+ updateDropdownHighlight();
6916
+ } else if (e.key === "Enter" && filtered.length > 0) {
6917
+ e.preventDefault();
6918
+ insertMention(filtered[dropdownState.selectedIndex]);
6919
+ } else if (e.key === "Escape") {
6920
+ closeDropdown();
6921
+ }
6922
+ });
6923
+ document.addEventListener(
6924
+ "click",
6925
+ (e) => {
6926
+ if (!outerDiv.contains(e.target) && !dropdown.contains(e.target)) {
6927
+ closeDropdown();
6928
+ }
6929
+ },
6930
+ { signal: docListenerCtrl.signal }
6931
+ );
6932
+ function renderFilesRow() {
6933
+ clear(filesRow);
6934
+ if (files.length === 0) {
6935
+ filesRow.style.display = "none";
6936
+ return;
6937
+ }
6938
+ filesRow.style.display = "flex";
6939
+ files.forEach((rid) => {
6940
+ const meta = state.resourceIndex.get(rid);
6941
+ const thumbWrapper = document.createElement("div");
6942
+ thumbWrapper.className = "fb-richinput-file-thumb";
6943
+ thumbWrapper.style.cssText = `
6944
+ position: relative;
6945
+ width: 48px;
6946
+ height: 48px;
6947
+ border: 1px solid var(--fb-border-color, #d1d5db);
6948
+ border-radius: 8px;
6949
+ overflow: hidden;
6950
+ flex-shrink: 0;
6951
+ cursor: pointer;
6952
+ background: #fff;
6953
+ `;
6954
+ const thumbInner = document.createElement("div");
6955
+ thumbInner.style.cssText = "width: 48px; height: 48px; border-radius: inherit; overflow: hidden;";
6956
+ renderThumbContent(thumbInner, rid, meta, state);
6957
+ thumbWrapper.appendChild(thumbInner);
6958
+ const tooltipHandle = createTooltipHandle();
6959
+ const doMention = () => {
6960
+ var _a2, _b, _c;
6961
+ const cursorPos = (_a2 = textarea.selectionStart) != null ? _a2 : textarea.value.length;
6962
+ const labels = buildFileLabelsFromClosure();
6963
+ const label = (_c = (_b = labels.get(rid)) != null ? _b : meta == null ? void 0 : meta.name) != null ? _c : rid;
6964
+ const before = textarea.value.slice(0, cursorPos);
6965
+ const after = textarea.value.slice(cursorPos);
6966
+ const prefix = before.length > 0 && !/[\s\n]$/.test(before) ? "\n" : "";
6967
+ const mention = `${prefix}${formatMention(label)} `;
6968
+ textarea.value = `${before}${mention}${after}`;
6969
+ const newPos = cursorPos + mention.length;
6970
+ textarea.setSelectionRange(newPos, newPos);
6971
+ textarea.focus();
6972
+ textarea.dispatchEvent(new Event("input"));
6973
+ };
6974
+ const doRemove = () => {
6975
+ var _a2;
6976
+ const idx = files.indexOf(rid);
6977
+ if (idx !== -1) files.splice(idx, 1);
6978
+ renderFilesRow();
6979
+ updateBackdrop();
6980
+ writeHidden();
6981
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
6982
+ };
6983
+ thumbWrapper.addEventListener("mouseenter", () => {
6984
+ cancelHideTooltip(tooltipHandle);
6985
+ if (!tooltipHandle.element) {
6986
+ tooltipHandle.element = showFileTooltip(thumbWrapper, {
6987
+ rid,
6988
+ state,
6989
+ isReadonly: false,
6990
+ onMention: doMention,
6991
+ onRemove: doRemove
6992
+ });
6993
+ tooltipHandle.element.addEventListener("mouseenter", () => {
6994
+ cancelHideTooltip(tooltipHandle);
6995
+ });
6996
+ tooltipHandle.element.addEventListener("mouseleave", () => {
6997
+ scheduleHideTooltip(tooltipHandle);
6998
+ });
6999
+ }
7000
+ });
7001
+ thumbWrapper.addEventListener("mouseleave", () => {
7002
+ scheduleHideTooltip(tooltipHandle);
7003
+ });
7004
+ filesRow.appendChild(thumbWrapper);
7005
+ });
7006
+ }
7007
+ function uploadFile(file) {
7008
+ if (!state.config.uploadFile) return;
7009
+ const tempId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
7010
+ state.resourceIndex.set(tempId, {
7011
+ name: file.name,
7012
+ type: file.type,
7013
+ size: file.size,
7014
+ uploadedAt: /* @__PURE__ */ new Date(),
7015
+ file
7016
+ });
7017
+ files.push(tempId);
7018
+ renderFilesRow();
7019
+ const thumbs = filesRow.querySelectorAll(
7020
+ ".fb-richinput-file-thumb"
7021
+ );
7022
+ const loadingThumb = thumbs[thumbs.length - 1];
7023
+ if (loadingThumb) loadingThumb.style.opacity = "0.5";
7024
+ state.config.uploadFile(file).then((resourceId) => {
7025
+ var _a2;
7026
+ const idx = files.indexOf(tempId);
7027
+ if (idx !== -1) files[idx] = resourceId;
7028
+ state.resourceIndex.delete(tempId);
7029
+ state.resourceIndex.set(resourceId, {
7030
+ name: file.name,
7031
+ type: file.type,
7032
+ size: file.size,
7033
+ uploadedAt: /* @__PURE__ */ new Date(),
7034
+ file
7035
+ });
7036
+ renderFilesRow();
7037
+ updateBackdrop();
7038
+ writeHidden();
7039
+ (_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
7040
+ }).catch((err) => {
7041
+ var _a2, _b;
7042
+ const idx = files.indexOf(tempId);
7043
+ if (idx !== -1) files.splice(idx, 1);
7044
+ state.resourceIndex.delete(tempId);
7045
+ renderFilesRow();
7046
+ (_b = (_a2 = state.config).onUploadError) == null ? void 0 : _b.call(_a2, err, file);
7047
+ });
7048
+ }
7049
+ fileInput.addEventListener("change", () => {
7050
+ var _a2;
7051
+ const selected = fileInput.files;
7052
+ if (!selected || selected.length === 0) return;
7053
+ const maxFiles = (_a2 = element.maxFiles) != null ? _a2 : Infinity;
7054
+ for (let i = 0; i < selected.length && files.length < maxFiles; i++) {
7055
+ uploadFile(selected[i]);
7056
+ }
7057
+ fileInput.value = "";
7058
+ });
7059
+ textareaArea.appendChild(backdrop);
7060
+ textareaArea.appendChild(textarea);
7061
+ textareaArea.appendChild(paperclipBtn);
7062
+ textareaArea.appendChild(dropdown);
7063
+ outerDiv.appendChild(filesRow);
7064
+ outerDiv.appendChild(textareaArea);
7065
+ if (element.minLength != null || element.maxLength != null) {
7066
+ const counterRow = document.createElement("div");
7067
+ counterRow.style.cssText = "position: relative; padding: 2px 14px 6px; text-align: right;";
7068
+ const counter = createCharCounter(element, textarea, false);
7069
+ counter.style.cssText = `
7070
+ position: static;
7071
+ display: inline-block;
7072
+ font-size: var(--fb-font-size-small);
7073
+ color: var(--fb-text-secondary-color);
7074
+ pointer-events: none;
7075
+ `;
7076
+ counterRow.appendChild(counter);
7077
+ outerDiv.appendChild(counterRow);
7078
+ }
7079
+ outerDiv.appendChild(hiddenInput);
7080
+ outerDiv.appendChild(fileInput);
7081
+ writeHidden();
7082
+ updateBackdrop();
7083
+ hiddenInput._applyExternalUpdate = (value) => {
7084
+ var _a2;
7085
+ const rawText = (_a2 = value.text) != null ? _a2 : "";
7086
+ textarea.value = rawText ? replaceRidsWithFilenames(rawText, files, state) : "";
7087
+ textarea.dispatchEvent(new Event("input"));
7088
+ files.length = 0;
7089
+ for (const rid of value.files) files.push(rid);
7090
+ renderFilesRow();
7091
+ updateBackdrop();
7092
+ writeHidden();
7093
+ };
7094
+ wrapper.appendChild(outerDiv);
7095
+ renderFilesRow();
7096
+ const observer = new MutationObserver(() => {
7097
+ if (!outerDiv.isConnected) {
7098
+ docListenerCtrl.abort();
7099
+ mentionTooltip = removePortalTooltip(mentionTooltip);
7100
+ observer.disconnect();
7101
+ }
7102
+ });
7103
+ if (outerDiv.parentElement) {
7104
+ observer.observe(outerDiv.parentElement, { childList: true });
7105
+ }
7106
+ }
7107
+ function renderReadonlyMode(_element, ctx, wrapper, _pathKey, value) {
7108
+ var _a, _b;
7109
+ const state = ctx.state;
7110
+ const { text, files } = value;
7111
+ const ridToName = /* @__PURE__ */ new Map();
7112
+ for (const rid of files) {
7113
+ const meta = state.resourceIndex.get(rid);
7114
+ if (meta == null ? void 0 : meta.name) ridToName.set(rid, meta.name);
7115
+ }
7116
+ if (files.length > 0) {
7117
+ const filesRow = document.createElement("div");
7118
+ filesRow.style.cssText = "display: flex; flex-wrap: wrap; gap: 6px; padding-bottom: 8px;";
7119
+ files.forEach((rid) => {
7120
+ const meta = state.resourceIndex.get(rid);
7121
+ const thumbWrapper = document.createElement("div");
7122
+ thumbWrapper.style.cssText = `
7123
+ position: relative;
7124
+ width: 48px; height: 48px;
7125
+ border: 1px solid var(--fb-border-color, #d1d5db);
7126
+ border-radius: 8px;
7127
+ overflow: hidden;
7128
+ flex-shrink: 0;
7129
+ background: #fff;
7130
+ cursor: default;
7131
+ `;
7132
+ const thumbInner = document.createElement("div");
7133
+ thumbInner.style.cssText = "width: 48px; height: 48px; border-radius: inherit; overflow: hidden;";
7134
+ renderThumbContent(thumbInner, rid, meta, state);
7135
+ thumbWrapper.appendChild(thumbInner);
7136
+ const tooltipHandle = createTooltipHandle();
7137
+ thumbWrapper.addEventListener("mouseenter", () => {
7138
+ cancelHideTooltip(tooltipHandle);
7139
+ if (!tooltipHandle.element) {
7140
+ tooltipHandle.element = showFileTooltip(thumbWrapper, {
7141
+ rid,
7142
+ state,
7143
+ isReadonly: true
7144
+ });
7145
+ tooltipHandle.element.addEventListener("mouseenter", () => {
7146
+ cancelHideTooltip(tooltipHandle);
7147
+ });
7148
+ tooltipHandle.element.addEventListener("mouseleave", () => {
7149
+ scheduleHideTooltip(tooltipHandle);
7150
+ });
7151
+ }
7152
+ });
7153
+ thumbWrapper.addEventListener("mouseleave", () => {
7154
+ scheduleHideTooltip(tooltipHandle);
7155
+ });
7156
+ filesRow.appendChild(thumbWrapper);
7157
+ });
7158
+ wrapper.appendChild(filesRow);
7159
+ }
7160
+ if (text) {
7161
+ const textDiv = document.createElement("div");
7162
+ textDiv.style.cssText = `
7163
+ ${TEXTAREA_FONT}
7164
+ color: var(--fb-text-color, #111827);
7165
+ white-space: pre-wrap;
7166
+ word-break: break-word;
7167
+ `;
7168
+ const tokens = findAtTokens(text);
7169
+ const resolvedTokens = tokens.filter(
7170
+ (tok) => ridToName.has(tok.name) || [...ridToName.values()].includes(tok.name)
7171
+ );
7172
+ if (resolvedTokens.length === 0) {
7173
+ textDiv.textContent = text;
7174
+ } else {
7175
+ let lastIndex = 0;
7176
+ for (const token of resolvedTokens) {
7177
+ if (token.start > lastIndex) {
7178
+ textDiv.appendChild(
7179
+ document.createTextNode(text.slice(lastIndex, token.start))
7180
+ );
7181
+ }
7182
+ const span = document.createElement("span");
7183
+ span.style.cssText = `
7184
+ display: inline;
7185
+ background: color-mix(in srgb, var(--fb-primary-color, #0066cc) 15%, transparent);
7186
+ color: var(--fb-primary-color, #0066cc);
7187
+ border-radius: 8px;
7188
+ padding: 1px 6px;
7189
+ font-weight: 500;
7190
+ cursor: default;
7191
+ `;
7192
+ const rid = ridToName.has(token.name) ? token.name : (_a = [...ridToName.entries()].find(([, n]) => n === token.name)) == null ? void 0 : _a[0];
7193
+ const displayName = (_b = ridToName.get(token.name)) != null ? _b : token.name;
7194
+ span.textContent = `@${displayName}`;
7195
+ if (rid) {
7196
+ let spanTooltip = null;
7197
+ const mentionRid = rid;
7198
+ span.addEventListener("mouseenter", () => {
7199
+ spanTooltip = removePortalTooltip(spanTooltip);
7200
+ spanTooltip = showMentionTooltip(span, mentionRid, state);
7201
+ });
7202
+ span.addEventListener("mouseleave", () => {
7203
+ spanTooltip = removePortalTooltip(spanTooltip);
7204
+ });
7205
+ }
7206
+ textDiv.appendChild(span);
7207
+ lastIndex = token.end;
7208
+ }
7209
+ if (lastIndex < text.length) {
7210
+ textDiv.appendChild(document.createTextNode(text.slice(lastIndex)));
7211
+ }
7212
+ }
7213
+ wrapper.appendChild(textDiv);
7214
+ }
7215
+ if (!text && files.length === 0) {
7216
+ const empty = document.createElement("div");
7217
+ empty.style.cssText = "color: var(--fb-text-muted-color, #6b7280); font-size: var(--fb-font-size, 14px);";
7218
+ empty.textContent = "\u2014";
7219
+ wrapper.appendChild(empty);
7220
+ }
7221
+ }
7222
+ function renderRichInputElement(element, ctx, wrapper, pathKey) {
7223
+ var _a, _b, _c, _d;
7224
+ const state = ctx.state;
7225
+ const textKey = (_a = element.textKey) != null ? _a : "text";
7226
+ const filesKey = (_b = element.filesKey) != null ? _b : "files";
7227
+ const rawPrefill = ctx.prefill[element.key];
7228
+ let initialValue;
7229
+ if (rawPrefill && typeof rawPrefill === "object" && !Array.isArray(rawPrefill)) {
7230
+ const obj = rawPrefill;
7231
+ const textVal = (_c = obj[textKey]) != null ? _c : obj["text"];
7232
+ const filesVal = (_d = obj[filesKey]) != null ? _d : obj["files"];
7233
+ initialValue = {
7234
+ text: typeof textVal === "string" ? textVal : null,
7235
+ files: Array.isArray(filesVal) ? filesVal : []
7236
+ };
7237
+ } else if (typeof rawPrefill === "string") {
7238
+ initialValue = { text: rawPrefill || null, files: [] };
7239
+ } else {
7240
+ initialValue = { text: null, files: [] };
7241
+ }
7242
+ for (const rid of initialValue.files) {
7243
+ if (!state.resourceIndex.has(rid)) {
7244
+ state.resourceIndex.set(rid, {
7245
+ name: rid,
7246
+ type: "application/octet-stream",
7247
+ size: 0,
7248
+ uploadedAt: /* @__PURE__ */ new Date(),
7249
+ file: void 0
7250
+ });
7251
+ }
7252
+ }
7253
+ if (state.config.readonly) {
7254
+ renderReadonlyMode(element, ctx, wrapper, pathKey, initialValue);
7255
+ } else {
7256
+ if (!state.config.uploadFile) {
7257
+ throw new Error(
7258
+ `RichInput field "${element.key}" requires uploadFile handler in config`
7259
+ );
7260
+ }
7261
+ renderEditMode(element, ctx, wrapper, pathKey, initialValue);
7262
+ }
7263
+ }
7264
+ function validateRichInputElement(element, key, context) {
7265
+ var _a, _b;
7266
+ const { scopeRoot, state, skipValidation } = context;
7267
+ const errors = [];
7268
+ const textKey = (_a = element.textKey) != null ? _a : "text";
7269
+ const filesKey = (_b = element.filesKey) != null ? _b : "files";
7270
+ const hiddenInput = scopeRoot.querySelector(
7271
+ `[name="${key}"]`
7272
+ );
7273
+ if (!hiddenInput) {
7274
+ return { value: null, errors };
7275
+ }
7276
+ let rawValue = {};
7277
+ try {
7278
+ const parsed = JSON.parse(hiddenInput.value);
7279
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7280
+ rawValue = parsed;
7281
+ } else {
7282
+ errors.push(`${key}: invalid richinput data`);
7283
+ return { value: null, errors };
7284
+ }
7285
+ } catch {
7286
+ errors.push(`${key}: invalid richinput data`);
7287
+ return { value: null, errors };
7288
+ }
7289
+ const textVal = rawValue[textKey];
7290
+ const filesVal = rawValue[filesKey];
7291
+ const text = textVal === null || typeof textVal === "string" ? textVal : null;
7292
+ const files = Array.isArray(filesVal) ? filesVal : [];
7293
+ const value = {
7294
+ [textKey]: text != null ? text : null,
7295
+ [filesKey]: files
7296
+ };
7297
+ if (!skipValidation) {
7298
+ const textEmpty = !text || text.trim() === "";
7299
+ const filesEmpty = files.length === 0;
7300
+ if (element.required && textEmpty && filesEmpty) {
7301
+ errors.push(`${key}: ${t("required", state)}`);
7302
+ }
7303
+ if (!textEmpty && text) {
7304
+ if (element.minLength != null && text.length < element.minLength) {
7305
+ errors.push(
7306
+ `${key}: ${t("minLength", state, { min: element.minLength })}`
7307
+ );
7308
+ }
7309
+ if (element.maxLength != null && text.length > element.maxLength) {
7310
+ errors.push(
7311
+ `${key}: ${t("maxLength", state, { max: element.maxLength })}`
7312
+ );
7313
+ }
7314
+ }
7315
+ if (element.maxFiles != null && files.length > element.maxFiles) {
7316
+ errors.push(
7317
+ `${key}: ${t("maxFiles", state, { max: element.maxFiles })}`
7318
+ );
7319
+ }
7320
+ }
7321
+ return { value, errors };
7322
+ }
7323
+ function updateRichInputField(element, fieldPath, value, context) {
7324
+ var _a, _b, _c, _d;
7325
+ const { scopeRoot } = context;
7326
+ const hiddenInput = scopeRoot.querySelector(
7327
+ `[name="${fieldPath}"]`
7328
+ );
7329
+ if (!hiddenInput) {
7330
+ console.warn(
7331
+ `updateRichInputField: no hidden input found for "${fieldPath}". Re-render to reflect new data.`
7332
+ );
7333
+ return;
7334
+ }
7335
+ let normalized = null;
7336
+ if (value && typeof value === "object" && !Array.isArray(value)) {
7337
+ const obj = value;
7338
+ const textKey = (_a = element.textKey) != null ? _a : "text";
7339
+ const filesKey = (_b = element.filesKey) != null ? _b : "files";
7340
+ const textVal = (_c = obj[textKey]) != null ? _c : obj["text"];
7341
+ const filesVal = (_d = obj[filesKey]) != null ? _d : obj["files"];
7342
+ if (textVal !== void 0 || filesVal !== void 0) {
7343
+ normalized = {
7344
+ text: typeof textVal === "string" ? textVal : null,
7345
+ files: Array.isArray(filesVal) ? filesVal : []
7346
+ };
7347
+ }
7348
+ }
7349
+ if (normalized && hiddenInput._applyExternalUpdate) {
7350
+ hiddenInput._applyExternalUpdate(normalized);
7351
+ } else if (normalized) {
7352
+ hiddenInput.value = JSON.stringify(normalized);
7353
+ }
7354
+ }
7355
+
5796
7356
  // src/components/index.ts
5797
7357
  function showTooltip(tooltipId, button) {
5798
7358
  const tooltip = document.getElementById(tooltipId);
@@ -6145,6 +7705,9 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
6145
7705
  case "table":
6146
7706
  renderTableElement(element, ctx, wrapper, pathKey);
6147
7707
  break;
7708
+ case "richinput":
7709
+ renderRichInputElement(element, ctx, wrapper, pathKey);
7710
+ break;
6148
7711
  default: {
6149
7712
  const unsupported = document.createElement("div");
6150
7713
  unsupported.className = "text-red-500 text-sm";
@@ -6189,6 +7752,7 @@ var defaultConfig = {
6189
7752
  enableFilePreview: true,
6190
7753
  maxPreviewSize: "200px",
6191
7754
  readonly: false,
7755
+ parseTableFile: null,
6192
7756
  locale: "en",
6193
7757
  translations: {
6194
7758
  en: {
@@ -6241,7 +7805,14 @@ var defaultConfig = {
6241
7805
  tableRemoveRow: "Remove row",
6242
7806
  tableRemoveColumn: "Remove column",
6243
7807
  tableMergeCells: "Merge cells (Ctrl+M)",
6244
- tableSplitCell: "Split cell (Ctrl+Shift+M)"
7808
+ tableSplitCell: "Split cell (Ctrl+Shift+M)",
7809
+ tableImportFile: "Import",
7810
+ tableImporting: "Importing...",
7811
+ tableImportError: "Import failed: {error}",
7812
+ richinputPlaceholder: "Type text...",
7813
+ richinputAttachFile: "Attach file",
7814
+ richinputMention: "Mention",
7815
+ richinputRemoveFile: "Remove"
6245
7816
  },
6246
7817
  ru: {
6247
7818
  // UI texts
@@ -6293,7 +7864,14 @@ var defaultConfig = {
6293
7864
  tableRemoveRow: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443",
6294
7865
  tableRemoveColumn: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u043E\u043B\u0431\u0435\u0446",
6295
7866
  tableMergeCells: "\u041E\u0431\u044A\u0435\u0434\u0438\u043D\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0438 (Ctrl+M)",
6296
- tableSplitCell: "\u0420\u0430\u0437\u0434\u0435\u043B\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0443 (Ctrl+Shift+M)"
7867
+ tableSplitCell: "\u0420\u0430\u0437\u0434\u0435\u043B\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0443 (Ctrl+Shift+M)",
7868
+ tableImportFile: "\u0418\u043C\u043F\u043E\u0440\u0442",
7869
+ tableImporting: "\u0418\u043C\u043F\u043E\u0440\u0442...",
7870
+ tableImportError: "\u041E\u0448\u0438\u0431\u043A\u0430 \u0438\u043C\u043F\u043E\u0440\u0442\u0430: {error}",
7871
+ richinputPlaceholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442...",
7872
+ richinputAttachFile: "\u041F\u0440\u0438\u043A\u0440\u0435\u043F\u0438\u0442\u044C \u0444\u0430\u0439\u043B",
7873
+ richinputMention: "\u0423\u043F\u043E\u043C\u044F\u043D\u0443\u0442\u044C",
7874
+ richinputRemoveFile: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C"
6297
7875
  }
6298
7876
  },
6299
7877
  theme: {}
@@ -6571,6 +8149,10 @@ var componentRegistry = {
6571
8149
  table: {
6572
8150
  validate: validateTableElement,
6573
8151
  update: updateTableField
8152
+ },
8153
+ richinput: {
8154
+ validate: validateRichInputElement,
8155
+ update: updateRichInputField
6574
8156
  }
6575
8157
  };
6576
8158
  function getComponentOperations(elementType) {