@bendyline/squisq-editor-react 2.11.14 → 2.11.15
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/dist/{chunk-QY2ZFRJE.js → chunk-7B46MJNW.js} +333 -140
- package/dist/{chunk-V44VP242.js → chunk-K4Z2GIX4.js} +29 -14
- package/dist/image-editor/index.js +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +2 -2
- package/dist/shell/index.d.ts +1 -1
- package/dist/shell/index.js +2 -2
- package/dist/{shell-3UAmXcYE.d.ts → shell-X0EoQbbN.d.ts} +9 -3
- package/package.json +7 -7
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
ImageEditor,
|
|
34
34
|
ImageViewer,
|
|
35
35
|
ShapePalette
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-K4Z2GIX4.js";
|
|
37
37
|
import {
|
|
38
38
|
BlockThumbnail,
|
|
39
39
|
RecorderPanel,
|
|
@@ -1026,7 +1026,7 @@ function VersionHistoryPanel() {
|
|
|
1026
1026
|
}
|
|
1027
1027
|
),
|
|
1028
1028
|
visibleVersions.map((v2) => {
|
|
1029
|
-
const
|
|
1029
|
+
const pending2 = state.pendingRevert !== null && state.pendingRevert.path === v2.path;
|
|
1030
1030
|
const selected = state.selected?.version.path === v2.path;
|
|
1031
1031
|
return /* @__PURE__ */ jsxs(
|
|
1032
1032
|
"li",
|
|
@@ -1054,7 +1054,7 @@ function VersionHistoryPanel() {
|
|
|
1054
1054
|
children: "Revert"
|
|
1055
1055
|
}
|
|
1056
1056
|
) }),
|
|
1057
|
-
|
|
1057
|
+
pending2 && /* @__PURE__ */ jsxs("div", { className: "squisq-version-history-confirm", children: [
|
|
1058
1058
|
/* @__PURE__ */ jsx("span", { children: "Revert to this version? Your current draft will be saved as a new snapshot first. Reverting will reset your editor undo state." }),
|
|
1059
1059
|
/* @__PURE__ */ jsxs("span", { className: "squisq-version-history-confirm-actions", children: [
|
|
1060
1060
|
/* @__PURE__ */ jsx(
|
|
@@ -27541,15 +27541,15 @@ async function loadDataPreview(bytes, ext, opts) {
|
|
|
27541
27541
|
var previewCache = /* @__PURE__ */ new Map();
|
|
27542
27542
|
function loadDataPreviewCached(path, revision, load) {
|
|
27543
27543
|
const key = `${revision}\0${path}`;
|
|
27544
|
-
let
|
|
27545
|
-
if (!
|
|
27544
|
+
let pending2 = previewCache.get(key);
|
|
27545
|
+
if (!pending2) {
|
|
27546
27546
|
for (const existing of previewCache.keys()) {
|
|
27547
27547
|
if (existing.endsWith(`\0${path}`) && existing !== key) previewCache.delete(existing);
|
|
27548
27548
|
}
|
|
27549
|
-
|
|
27550
|
-
previewCache.set(key,
|
|
27549
|
+
pending2 = load();
|
|
27550
|
+
previewCache.set(key, pending2);
|
|
27551
27551
|
}
|
|
27552
|
-
return
|
|
27552
|
+
return pending2;
|
|
27553
27553
|
}
|
|
27554
27554
|
|
|
27555
27555
|
// src/dataCard/ingestAdapters.ts
|
|
@@ -27591,20 +27591,25 @@ async function ingestSidecarBytes(bytes, ext, params) {
|
|
|
27591
27591
|
};
|
|
27592
27592
|
}
|
|
27593
27593
|
if (ext === "xlsx") {
|
|
27594
|
-
const { xlsxToCellGrids, gridToTables, parseCellRef } = await import("@bendyline/squisq-formats/xlsx");
|
|
27595
|
-
const grids = await xlsxToCellGrids(bytes
|
|
27596
|
-
...params.sheet !== void 0 ? { sheet: params.sheet } : {}
|
|
27597
|
-
});
|
|
27594
|
+
const { xlsxToCellGrids, gridToTables, parseCellRef, formatCellRef } = await import("@bendyline/squisq-formats/xlsx");
|
|
27595
|
+
const grids = await xlsxToCellGrids(bytes);
|
|
27598
27596
|
const bySheet = new Map(grids.sheets.map((sheet) => [sheet.name, sheet]));
|
|
27599
|
-
const tables = grids.sheets.flatMap(
|
|
27600
|
-
(sheet) => gridToTables(sheet.name, sheet.cells, sheet.merges)
|
|
27601
|
-
);
|
|
27597
|
+
const tables = grids.sheets.filter((sheet) => params.sheet === void 0 || sheet.name === params.sheet).flatMap((sheet) => gridToTables(sheet.name, sheet.cells, sheet.merges));
|
|
27602
27598
|
const table = params.anchor && tables.find((t) => t.anchor.toUpperCase() === params.anchor.toUpperCase()) || tables.reduce(
|
|
27603
27599
|
(best, candidate) => !best || candidate.rows.length * candidate.columns.length > best.rows.length * best.columns.length ? candidate : best,
|
|
27604
27600
|
null
|
|
27605
27601
|
);
|
|
27606
27602
|
if (!table) throw new Error("no tabular data found in workbook");
|
|
27607
27603
|
const anchor = parseCellRef(table.anchor);
|
|
27604
|
+
if (params.headerRow === false && table.hasHeader) {
|
|
27605
|
+
table.rows.unshift(table.columns.map((column) => column.name));
|
|
27606
|
+
table.columns = table.columns.map((column, col) => ({
|
|
27607
|
+
...column,
|
|
27608
|
+
name: formatCellRef(0, (anchor?.col ?? 0) + col).slice(0, -1),
|
|
27609
|
+
kind: "mixed"
|
|
27610
|
+
}));
|
|
27611
|
+
table.hasHeader = false;
|
|
27612
|
+
}
|
|
27608
27613
|
const sheetGrid = bySheet.get(table.sheet);
|
|
27609
27614
|
const locked = /* @__PURE__ */ new Set();
|
|
27610
27615
|
const dateLocked = /* @__PURE__ */ new Set();
|
|
@@ -27834,6 +27839,43 @@ import { EMPTY_TABLE_VIEW_STATE } from "@bendyline/squisq/table";
|
|
|
27834
27839
|
|
|
27835
27840
|
// src/dataCard/gridSave.ts
|
|
27836
27841
|
import { DATA_BACKUP_PREFIX, buildDataBackupPath } from "@bendyline/squisq/versions";
|
|
27842
|
+
|
|
27843
|
+
// src/dataCard/saveSession.ts
|
|
27844
|
+
var sessions = /* @__PURE__ */ new WeakMap();
|
|
27845
|
+
function withDataSave(options, save) {
|
|
27846
|
+
const owner = options.container ?? options.mediaProvider;
|
|
27847
|
+
let files = sessions.get(owner);
|
|
27848
|
+
if (!files) {
|
|
27849
|
+
files = /* @__PURE__ */ new Map();
|
|
27850
|
+
sessions.set(owner, files);
|
|
27851
|
+
}
|
|
27852
|
+
let session = files.get(options.path);
|
|
27853
|
+
if (!session) {
|
|
27854
|
+
session = { tail: Promise.resolve() };
|
|
27855
|
+
files.set(options.path, session);
|
|
27856
|
+
}
|
|
27857
|
+
const current = session;
|
|
27858
|
+
const result = current.tail.catch(() => void 0).then(async () => {
|
|
27859
|
+
let latest = await options.container?.readFile(options.path);
|
|
27860
|
+
if (!latest && !options.container) {
|
|
27861
|
+
const url = await options.mediaProvider.resolveUrl(options.path);
|
|
27862
|
+
if (url !== options.path) {
|
|
27863
|
+
const response = await fetch(url);
|
|
27864
|
+
if (!response.ok)
|
|
27865
|
+
throw new Error(`Could not read the current file before saving (${response.status})`);
|
|
27866
|
+
latest = await response.arrayBuffer();
|
|
27867
|
+
}
|
|
27868
|
+
}
|
|
27869
|
+
const baseline = latest ?? current.bytes ?? options.originalBytes;
|
|
27870
|
+
const saved = await save(baseline);
|
|
27871
|
+
if (saved.ok && saved.bytes) current.bytes = saved.bytes;
|
|
27872
|
+
return saved;
|
|
27873
|
+
});
|
|
27874
|
+
current.tail = result;
|
|
27875
|
+
return result;
|
|
27876
|
+
}
|
|
27877
|
+
|
|
27878
|
+
// src/dataCard/gridSave.ts
|
|
27837
27879
|
var BACKUPS_KEPT_PER_SIDECAR = 3;
|
|
27838
27880
|
var FORMULA_PREFIX = /^[\t\r\n \uFEFF]*[=+\-@]/;
|
|
27839
27881
|
function renderCell(value) {
|
|
@@ -27856,96 +27898,111 @@ async function backupOriginal(container, path, original, mime) {
|
|
|
27856
27898
|
}
|
|
27857
27899
|
}
|
|
27858
27900
|
async function saveCsvEdits(options) {
|
|
27859
|
-
const { path,
|
|
27860
|
-
const
|
|
27861
|
-
|
|
27862
|
-
|
|
27863
|
-
|
|
27864
|
-
|
|
27865
|
-
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
|
|
27869
|
-
|
|
27870
|
-
|
|
27871
|
-
const
|
|
27872
|
-
|
|
27873
|
-
const
|
|
27874
|
-
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
27883
|
-
|
|
27884
|
-
|
|
27885
|
-
|
|
27886
|
-
|
|
27901
|
+
const { path, csv, journal, mediaProvider, container } = options;
|
|
27902
|
+
const snapshot = journal.entries();
|
|
27903
|
+
return withDataSave(options, async (originalBytes) => {
|
|
27904
|
+
const notices = [];
|
|
27905
|
+
const mime = path.toLowerCase().endsWith(".tsv") ? "text/tab-separated-values" : "text/csv";
|
|
27906
|
+
try {
|
|
27907
|
+
if (container) {
|
|
27908
|
+
await backupOriginal(container, path, originalBytes, mime);
|
|
27909
|
+
} else {
|
|
27910
|
+
notices.push("no workspace container \u2014 saved without a backup copy");
|
|
27911
|
+
}
|
|
27912
|
+
const { parseCsv, serializeCsvRows } = await import("@bendyline/squisq-formats/csv");
|
|
27913
|
+
const rows = parseCsv(new TextDecoder().decode(originalBytes), csv.delimiter);
|
|
27914
|
+
const offset = csv.hasHeader ? 1 : 0;
|
|
27915
|
+
for (const entry of snapshot) {
|
|
27916
|
+
const targetRow = entry.rowId + offset;
|
|
27917
|
+
while (rows.length <= targetRow) rows.push([]);
|
|
27918
|
+
const row = rows[targetRow];
|
|
27919
|
+
while (row.length <= entry.col) row.push("");
|
|
27920
|
+
row[entry.col] = neutralizeEdited(renderCell(entry.next));
|
|
27921
|
+
}
|
|
27922
|
+
const text = serializeCsvRows(rows, {
|
|
27923
|
+
delimiter: csv.delimiter,
|
|
27924
|
+
newline: csv.newline,
|
|
27925
|
+
trailingNewline: csv.trailingNewline,
|
|
27926
|
+
formulaHandling: "preserve"
|
|
27927
|
+
});
|
|
27928
|
+
const bytes = new TextEncoder().encode(csv.bom ? `\uFEFF${text}` : text).buffer;
|
|
27929
|
+
const savedPath = await mediaProvider.addMedia(path, bytes, mime);
|
|
27930
|
+
if (savedPath !== path) {
|
|
27931
|
+
return {
|
|
27932
|
+
ok: false,
|
|
27933
|
+
savedPath,
|
|
27934
|
+
notices,
|
|
27935
|
+
error: `the host saved a copy at "${savedPath}" instead of overwriting "${path}" \u2014 the document still references the original`
|
|
27936
|
+
};
|
|
27937
|
+
}
|
|
27938
|
+
journal.acknowledge(snapshot);
|
|
27939
|
+
return { ok: true, savedPath, notices, bytes };
|
|
27940
|
+
} catch (err) {
|
|
27887
27941
|
return {
|
|
27888
27942
|
ok: false,
|
|
27889
|
-
savedPath,
|
|
27890
27943
|
notices,
|
|
27891
|
-
error:
|
|
27944
|
+
error: err instanceof Error ? err.message : String(err)
|
|
27892
27945
|
};
|
|
27893
27946
|
}
|
|
27894
|
-
|
|
27895
|
-
|
|
27896
|
-
|
|
27897
|
-
|
|
27898
|
-
|
|
27899
|
-
notices,
|
|
27900
|
-
error: err instanceof Error ? err.message : String(err)
|
|
27901
|
-
};
|
|
27902
|
-
}
|
|
27947
|
+
}).catch((error) => ({
|
|
27948
|
+
ok: false,
|
|
27949
|
+
notices: [],
|
|
27950
|
+
error: error instanceof Error ? error.message : String(error)
|
|
27951
|
+
}));
|
|
27903
27952
|
}
|
|
27904
27953
|
var XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
27905
27954
|
async function saveXlsxEdits(options) {
|
|
27906
|
-
const { path,
|
|
27907
|
-
const
|
|
27908
|
-
|
|
27909
|
-
|
|
27910
|
-
|
|
27911
|
-
|
|
27912
|
-
|
|
27913
|
-
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
}));
|
|
27921
|
-
for (const [key, record] of formulaEdits ?? []) {
|
|
27922
|
-
const [row, col] = key.split(":").map(Number);
|
|
27923
|
-
patches.push({
|
|
27955
|
+
const { path, xlsx, journal, mediaProvider, container } = options;
|
|
27956
|
+
const snapshot = journal.entries();
|
|
27957
|
+
const formulaEdits = new Map(options.formulaEdits);
|
|
27958
|
+
return withDataSave(options, async (originalBytes) => {
|
|
27959
|
+
const notices = [];
|
|
27960
|
+
try {
|
|
27961
|
+
if (container) {
|
|
27962
|
+
await backupOriginal(container, path, originalBytes, XLSX_MIME);
|
|
27963
|
+
} else {
|
|
27964
|
+
notices.push("no workspace container \u2014 saved without a backup copy");
|
|
27965
|
+
}
|
|
27966
|
+
const { patchXlsxCellValues, formatCellRef } = await import("@bendyline/squisq-formats/xlsx");
|
|
27967
|
+
const bodyTop = xlsx.anchorRow + (xlsx.hasHeader ? 1 : 0);
|
|
27968
|
+
const patches = snapshot.filter((entry) => !formulaEdits.has(`${entry.rowId}:${entry.col}`)).map((entry) => ({
|
|
27924
27969
|
sheet: xlsx.sheet,
|
|
27925
|
-
ref: formatCellRef(bodyTop +
|
|
27926
|
-
|
|
27927
|
-
|
|
27928
|
-
|
|
27929
|
-
|
|
27930
|
-
|
|
27931
|
-
|
|
27932
|
-
|
|
27970
|
+
ref: formatCellRef(bodyTop + entry.rowId, xlsx.anchorCol + entry.col),
|
|
27971
|
+
value: entry.next
|
|
27972
|
+
}));
|
|
27973
|
+
for (const [key, record] of formulaEdits ?? []) {
|
|
27974
|
+
const [row, col] = key.split(":").map(Number);
|
|
27975
|
+
patches.push({
|
|
27976
|
+
sheet: xlsx.sheet,
|
|
27977
|
+
ref: formatCellRef(bodyTop + row, xlsx.anchorCol + col),
|
|
27978
|
+
...record.formula === null ? { value: record.value ?? null, removeFormula: true } : { formula: record.formula },
|
|
27979
|
+
...record.cachedValue !== void 0 ? { cachedValue: record.cachedValue } : {}
|
|
27980
|
+
});
|
|
27981
|
+
}
|
|
27982
|
+
const bytes = await patchXlsxCellValues(originalBytes, patches);
|
|
27983
|
+
const savedPath = await mediaProvider.addMedia(path, bytes, XLSX_MIME);
|
|
27984
|
+
if (savedPath !== path) {
|
|
27985
|
+
return {
|
|
27986
|
+
ok: false,
|
|
27987
|
+
savedPath,
|
|
27988
|
+
notices,
|
|
27989
|
+
error: `the host saved a copy at "${savedPath}" instead of overwriting "${path}" \u2014 the document still references the original`
|
|
27990
|
+
};
|
|
27991
|
+
}
|
|
27992
|
+
journal.acknowledge(snapshot);
|
|
27993
|
+
return { ok: true, savedPath, notices, bytes };
|
|
27994
|
+
} catch (err) {
|
|
27933
27995
|
return {
|
|
27934
27996
|
ok: false,
|
|
27935
|
-
savedPath,
|
|
27936
27997
|
notices,
|
|
27937
|
-
error:
|
|
27998
|
+
error: err instanceof Error ? err.message : String(err)
|
|
27938
27999
|
};
|
|
27939
28000
|
}
|
|
27940
|
-
|
|
27941
|
-
|
|
27942
|
-
|
|
27943
|
-
|
|
27944
|
-
|
|
27945
|
-
notices,
|
|
27946
|
-
error: err instanceof Error ? err.message : String(err)
|
|
27947
|
-
};
|
|
27948
|
-
}
|
|
28001
|
+
}).catch((error) => ({
|
|
28002
|
+
ok: false,
|
|
28003
|
+
notices: [],
|
|
28004
|
+
error: error instanceof Error ? error.message : String(error)
|
|
28005
|
+
}));
|
|
27949
28006
|
}
|
|
27950
28007
|
|
|
27951
28008
|
// src/dataCard/formulaSupport.ts
|
|
@@ -28002,8 +28059,15 @@ async function createXlsxFormulaSession(meta, options = {}) {
|
|
|
28002
28059
|
engine = calc.createInHouseEngine(engineConfig);
|
|
28003
28060
|
}
|
|
28004
28061
|
}
|
|
28005
|
-
await
|
|
28006
|
-
|
|
28062
|
+
const initial = await (async () => {
|
|
28063
|
+
try {
|
|
28064
|
+
await engine.loadWorkbook(buildSeed(calc, meta));
|
|
28065
|
+
return await engine.evaluateAll();
|
|
28066
|
+
} catch (error) {
|
|
28067
|
+
engine.dispose();
|
|
28068
|
+
throw error;
|
|
28069
|
+
}
|
|
28070
|
+
})();
|
|
28007
28071
|
if (initial.status === "budget-exceeded") {
|
|
28008
28072
|
engine.dispose();
|
|
28009
28073
|
return null;
|
|
@@ -28015,7 +28079,7 @@ async function createXlsxFormulaSession(meta, options = {}) {
|
|
|
28015
28079
|
col: meta.anchorCol + col
|
|
28016
28080
|
});
|
|
28017
28081
|
const formulas = new Map(meta.formulas);
|
|
28018
|
-
const edits = /* @__PURE__ */ new Map();
|
|
28082
|
+
const edits = options.edits ?? /* @__PURE__ */ new Map();
|
|
28019
28083
|
let disposed = false;
|
|
28020
28084
|
const readValues = async (keys) => {
|
|
28021
28085
|
const coords = keys.map((key) => key.split(":").map(Number));
|
|
@@ -28032,6 +28096,13 @@ async function createXlsxFormulaSession(meta, options = {}) {
|
|
|
28032
28096
|
const updates = [];
|
|
28033
28097
|
keyList.forEach((key, index2) => {
|
|
28034
28098
|
const raw = states[index2].value;
|
|
28099
|
+
const formula = formulas.get(key);
|
|
28100
|
+
if (formula !== void 0 && (edits.has(key) || before.get(key) !== displayValue(calc, raw))) {
|
|
28101
|
+
edits.set(key, {
|
|
28102
|
+
formula,
|
|
28103
|
+
...!calc.isCalcError(raw) && raw !== null ? { cachedValue: raw } : {}
|
|
28104
|
+
});
|
|
28105
|
+
}
|
|
28035
28106
|
if (calc.isCalcError(raw) && raw.code === "#NAME?") return;
|
|
28036
28107
|
const value = displayValue(calc, raw);
|
|
28037
28108
|
if (before.get(key) !== value) {
|
|
@@ -28041,7 +28112,13 @@ async function createXlsxFormulaSession(meta, options = {}) {
|
|
|
28041
28112
|
});
|
|
28042
28113
|
return updates;
|
|
28043
28114
|
};
|
|
28044
|
-
|
|
28115
|
+
let mutationTail = Promise.resolve();
|
|
28116
|
+
const serial = (operation) => {
|
|
28117
|
+
const result = mutationTail.catch(() => void 0).then(operation);
|
|
28118
|
+
mutationTail = result;
|
|
28119
|
+
return result;
|
|
28120
|
+
};
|
|
28121
|
+
const session = {
|
|
28045
28122
|
getFormula: (bodyRow, col) => formulas.get(`${bodyRow}:${col}`),
|
|
28046
28123
|
isCellLocked: (bodyRow, col) => {
|
|
28047
28124
|
const key = `${bodyRow}:${col}`;
|
|
@@ -28095,7 +28172,8 @@ async function createXlsxFormulaSession(meta, options = {}) {
|
|
|
28095
28172
|
if (disposed) return [];
|
|
28096
28173
|
const key = `${bodyRow}:${col}`;
|
|
28097
28174
|
formulas.delete(key);
|
|
28098
|
-
edits.
|
|
28175
|
+
if (meta.formulas.has(key)) edits.set(key, { formula: null, value });
|
|
28176
|
+
else edits.delete(key);
|
|
28099
28177
|
const before = await snapshotRegion();
|
|
28100
28178
|
engine.setCellValue(toAddress(bodyRow, col), value);
|
|
28101
28179
|
const result = await engine.evaluateAll();
|
|
@@ -28105,7 +28183,7 @@ async function createXlsxFormulaSession(meta, options = {}) {
|
|
|
28105
28183
|
get dirtyCount() {
|
|
28106
28184
|
return edits.size;
|
|
28107
28185
|
},
|
|
28108
|
-
formulaEdits: () => edits,
|
|
28186
|
+
formulaEdits: () => new Map(edits),
|
|
28109
28187
|
async discard() {
|
|
28110
28188
|
const before = await snapshotRegion();
|
|
28111
28189
|
const staleKeys = new Set(formulas.keys());
|
|
@@ -28115,29 +28193,49 @@ async function createXlsxFormulaSession(meta, options = {}) {
|
|
|
28115
28193
|
await engine.loadWorkbook(buildSeed(calc, meta));
|
|
28116
28194
|
await engine.evaluateAll();
|
|
28117
28195
|
for (const key of formulas.keys()) staleKeys.add(key);
|
|
28118
|
-
|
|
28119
|
-
},
|
|
28120
|
-
markSaved() {
|
|
28196
|
+
const updates = await diffAgainst(before, staleKeys);
|
|
28121
28197
|
edits.clear();
|
|
28198
|
+
return updates;
|
|
28199
|
+
},
|
|
28200
|
+
markSaved(snapshot = new Map(edits), baseline) {
|
|
28201
|
+
if (baseline) meta = baseline;
|
|
28202
|
+
for (const [key, record] of snapshot) {
|
|
28203
|
+
if (edits.get(key) === record) edits.delete(key);
|
|
28204
|
+
}
|
|
28122
28205
|
},
|
|
28123
28206
|
dispose() {
|
|
28124
28207
|
disposed = true;
|
|
28125
28208
|
engine.dispose();
|
|
28209
|
+
},
|
|
28210
|
+
flush: async () => {
|
|
28211
|
+
await mutationTail;
|
|
28126
28212
|
}
|
|
28127
28213
|
};
|
|
28214
|
+
return {
|
|
28215
|
+
...session,
|
|
28216
|
+
get dirtyCount() {
|
|
28217
|
+
return session.dirtyCount;
|
|
28218
|
+
},
|
|
28219
|
+
commitFormula: (...args) => serial(() => session.commitFormula(...args)),
|
|
28220
|
+
noteValueEdit: (...args) => serial(() => session.noteValueEdit(...args)),
|
|
28221
|
+
discard: () => serial(() => session.discard())
|
|
28222
|
+
};
|
|
28128
28223
|
}
|
|
28129
28224
|
|
|
28130
|
-
// src/dataCard/
|
|
28131
|
-
|
|
28132
|
-
var XLSX_SESSION_LOCKED_REASON = "Date cells and shared-formula masters stay locked; other formulas are editable";
|
|
28133
|
-
var gridModulePromise = null;
|
|
28225
|
+
// src/dataCard/loadGridModule.ts
|
|
28226
|
+
var pending = null;
|
|
28134
28227
|
function loadGridModule() {
|
|
28135
|
-
|
|
28136
|
-
|
|
28228
|
+
pending ?? (pending = import("@bendyline/squisq-grid-react").catch(() => {
|
|
28229
|
+
pending = null;
|
|
28137
28230
|
return null;
|
|
28138
28231
|
}));
|
|
28139
|
-
return
|
|
28232
|
+
return pending;
|
|
28140
28233
|
}
|
|
28234
|
+
|
|
28235
|
+
// src/dataCard/DataCardWidget.tsx
|
|
28236
|
+
import { jsx as jsx54, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
28237
|
+
var XLSX_SESSION_LOCKED_REASON = "Date cells and shared-formula masters stay locked; other formulas are editable";
|
|
28238
|
+
var pendingFormulas = /* @__PURE__ */ new WeakMap();
|
|
28141
28239
|
function useCardLink(editor, blockId) {
|
|
28142
28240
|
const [version, setVersion] = useState47(0);
|
|
28143
28241
|
useEffect38(() => {
|
|
@@ -28190,14 +28288,23 @@ function DataCardWidget({
|
|
|
28190
28288
|
const [view, setView] = useState47(EMPTY_TABLE_VIEW_STATE);
|
|
28191
28289
|
const [viewPersisted, setViewPersisted] = useState47(false);
|
|
28192
28290
|
const [saving, setSaving] = useState47(false);
|
|
28291
|
+
const savingRef = useRef39(false);
|
|
28193
28292
|
const [saveNotice, setSaveNotice] = useState47(null);
|
|
28194
28293
|
const [formulaDirty, setFormulaDirty] = useState47(0);
|
|
28195
28294
|
const gridRef = useRef39(null);
|
|
28196
28295
|
const href = link?.href ?? null;
|
|
28197
28296
|
const cardPos = link?.pos ?? null;
|
|
28297
|
+
const activeProvider = getMediaProvider();
|
|
28298
|
+
const activeContainer = getContainer?.() ?? null;
|
|
28299
|
+
const regionParams = href && cardPos !== null ? readHeadingViewBinding(editor, cardPos, href).params : {};
|
|
28300
|
+
const regionIdentity = JSON.stringify([
|
|
28301
|
+
regionParams.sheet,
|
|
28302
|
+
regionParams.anchor,
|
|
28303
|
+
regionParams.headerRow
|
|
28304
|
+
]);
|
|
28198
28305
|
useEffect38(() => {
|
|
28199
28306
|
if (!href || cardPos === null) return;
|
|
28200
|
-
const provider =
|
|
28307
|
+
const provider = activeProvider;
|
|
28201
28308
|
if (!provider) {
|
|
28202
28309
|
setState3({ kind: "meta", size: null, preview: null });
|
|
28203
28310
|
return;
|
|
@@ -28206,6 +28313,9 @@ function DataCardWidget({
|
|
|
28206
28313
|
setState3({ kind: "loading" });
|
|
28207
28314
|
void (async () => {
|
|
28208
28315
|
let size2 = null;
|
|
28316
|
+
let loadingStore = null;
|
|
28317
|
+
let loadingSession = null;
|
|
28318
|
+
let retained = false;
|
|
28209
28319
|
try {
|
|
28210
28320
|
const entries = await provider.listMedia();
|
|
28211
28321
|
const entry = entries.find((e2) => e2.name === href);
|
|
@@ -28249,26 +28359,61 @@ function DataCardWidget({
|
|
|
28249
28359
|
...binding.params.headerRow !== void 0 ? { headerRow: binding.params.headerRow !== "false" } : {}
|
|
28250
28360
|
});
|
|
28251
28361
|
if (cancelled) return;
|
|
28362
|
+
const container = activeContainer;
|
|
28363
|
+
const region = JSON.stringify(
|
|
28364
|
+
ingested.xlsx ? [
|
|
28365
|
+
ingested.xlsx.sheet,
|
|
28366
|
+
ingested.xlsx.anchorRow,
|
|
28367
|
+
ingested.xlsx.anchorCol,
|
|
28368
|
+
ingested.xlsx.hasHeader
|
|
28369
|
+
] : [ingested.csv?.hasHeader ?? true]
|
|
28370
|
+
);
|
|
28371
|
+
const journal = module.journalFor(href, revision, {
|
|
28372
|
+
owner: container ?? provider,
|
|
28373
|
+
region
|
|
28374
|
+
});
|
|
28375
|
+
let formulaEdits = pendingFormulas.get(journal);
|
|
28376
|
+
if (!formulaEdits) {
|
|
28377
|
+
formulaEdits = /* @__PURE__ */ new Map();
|
|
28378
|
+
pendingFormulas.set(journal, formulaEdits);
|
|
28379
|
+
}
|
|
28380
|
+
const restoredFormulas = new Map(formulaEdits);
|
|
28252
28381
|
let formulaSession = null;
|
|
28253
28382
|
if (ingested.xlsx) {
|
|
28254
28383
|
try {
|
|
28255
28384
|
const engineFactory = getCalcEngineFactory?.();
|
|
28256
|
-
formulaSession = await createXlsxFormulaSession(
|
|
28257
|
-
|
|
28258
|
-
|
|
28259
|
-
);
|
|
28385
|
+
formulaSession = await createXlsxFormulaSession(ingested.xlsx, {
|
|
28386
|
+
...engineFactory ? { engineFactory } : {},
|
|
28387
|
+
edits: formulaEdits
|
|
28388
|
+
});
|
|
28389
|
+
loadingSession = formulaSession;
|
|
28260
28390
|
} catch {
|
|
28261
28391
|
formulaSession = null;
|
|
28262
28392
|
}
|
|
28263
28393
|
}
|
|
28264
28394
|
if (cancelled) {
|
|
28265
|
-
formulaSession?.dispose();
|
|
28266
28395
|
return;
|
|
28267
28396
|
}
|
|
28268
|
-
gridRef.current?.formulaSession?.dispose();
|
|
28269
|
-
gridRef.current?.provider.dispose();
|
|
28270
28397
|
const storeProvider = new module.TableStoreClient(ingested.ingest);
|
|
28271
|
-
|
|
28398
|
+
loadingStore = storeProvider;
|
|
28399
|
+
await storeProvider.describe();
|
|
28400
|
+
const restoredValues = journal.entries().map(({ rowId, col, next }) => ({ rowId, col, value: next }));
|
|
28401
|
+
if (restoredValues.length) await storeProvider.applyEdits(restoredValues);
|
|
28402
|
+
if (formulaSession) {
|
|
28403
|
+
for (const edit of restoredValues) {
|
|
28404
|
+
if (restoredFormulas.has(`${edit.rowId}:${edit.col}`)) continue;
|
|
28405
|
+
const updates = await formulaSession.noteValueEdit(edit.rowId, edit.col, edit.value);
|
|
28406
|
+
if (updates.length) await storeProvider.applyEdits(updates);
|
|
28407
|
+
}
|
|
28408
|
+
for (const [key, record] of restoredFormulas) {
|
|
28409
|
+
const [row, col] = key.split(":").map(Number);
|
|
28410
|
+
const updates = record.formula === null ? await formulaSession.noteValueEdit(row, col, record.value ?? null) : (await formulaSession.commitFormula(row, col, record.formula)).updates ?? [];
|
|
28411
|
+
if (updates.length) await storeProvider.applyEdits(updates);
|
|
28412
|
+
}
|
|
28413
|
+
}
|
|
28414
|
+
if (cancelled) {
|
|
28415
|
+
return;
|
|
28416
|
+
}
|
|
28272
28417
|
const grid2 = {
|
|
28273
28418
|
module,
|
|
28274
28419
|
provider: storeProvider,
|
|
@@ -28276,13 +28421,19 @@ function DataCardWidget({
|
|
|
28276
28421
|
ingested,
|
|
28277
28422
|
bytes,
|
|
28278
28423
|
headers: ingested.ingest.headers,
|
|
28279
|
-
formulaSession
|
|
28424
|
+
formulaSession,
|
|
28425
|
+
mediaProvider: provider,
|
|
28426
|
+
container,
|
|
28427
|
+
path: href
|
|
28280
28428
|
};
|
|
28429
|
+
gridRef.current?.formulaSession?.dispose();
|
|
28430
|
+
gridRef.current?.provider.dispose();
|
|
28281
28431
|
gridRef.current = grid2;
|
|
28432
|
+
retained = true;
|
|
28282
28433
|
setView(viewStateFromBinding(binding, ingested.ingest.headers));
|
|
28283
28434
|
setViewPersisted(binding.persisted);
|
|
28284
28435
|
setSaveNotice(null);
|
|
28285
|
-
setFormulaDirty(0);
|
|
28436
|
+
setFormulaDirty(formulaSession?.dirtyCount ?? 0);
|
|
28286
28437
|
setState3({ kind: "grid", size: size2, grid: grid2 });
|
|
28287
28438
|
return;
|
|
28288
28439
|
}
|
|
@@ -28302,12 +28453,17 @@ function DataCardWidget({
|
|
|
28302
28453
|
reason: err instanceof Error ? err.message : String(err)
|
|
28303
28454
|
});
|
|
28304
28455
|
}
|
|
28456
|
+
} finally {
|
|
28457
|
+
if (!retained) {
|
|
28458
|
+
loadingStore?.dispose();
|
|
28459
|
+
loadingSession?.dispose();
|
|
28460
|
+
}
|
|
28305
28461
|
}
|
|
28306
28462
|
})();
|
|
28307
28463
|
return () => {
|
|
28308
28464
|
cancelled = true;
|
|
28309
28465
|
};
|
|
28310
|
-
}, [href, revision,
|
|
28466
|
+
}, [href, revision, activeProvider, activeContainer, regionIdentity, editor]);
|
|
28311
28467
|
useEffect38(
|
|
28312
28468
|
() => () => {
|
|
28313
28469
|
gridRef.current?.formulaSession?.dispose();
|
|
@@ -28327,34 +28483,61 @@ function DataCardWidget({
|
|
|
28327
28483
|
);
|
|
28328
28484
|
const handleSave = useCallback36(async () => {
|
|
28329
28485
|
const grid2 = gridRef.current;
|
|
28330
|
-
const provider =
|
|
28486
|
+
const provider = grid2?.mediaProvider;
|
|
28331
28487
|
if (!grid2 || !provider || !href || !grid2.ingested.csv && !grid2.ingested.xlsx) return;
|
|
28488
|
+
if (savingRef.current) return;
|
|
28489
|
+
savingRef.current = true;
|
|
28332
28490
|
setSaving(true);
|
|
28333
28491
|
try {
|
|
28492
|
+
await grid2.formulaSession?.flush();
|
|
28334
28493
|
const shared = {
|
|
28335
|
-
path:
|
|
28494
|
+
path: grid2.path,
|
|
28336
28495
|
originalBytes: grid2.bytes,
|
|
28337
28496
|
journal: grid2.journal,
|
|
28338
28497
|
mediaProvider: provider,
|
|
28339
|
-
container:
|
|
28498
|
+
container: grid2.container
|
|
28340
28499
|
};
|
|
28500
|
+
const formulaSnapshot = grid2.formulaSession?.formulaEdits();
|
|
28341
28501
|
const result = grid2.ingested.csv ? await saveCsvEdits({ ...shared, csv: grid2.ingested.csv }) : await saveXlsxEdits({
|
|
28342
28502
|
...shared,
|
|
28343
28503
|
xlsx: grid2.ingested.xlsx,
|
|
28344
|
-
...
|
|
28504
|
+
...formulaSnapshot ? { formulaEdits: formulaSnapshot } : {}
|
|
28345
28505
|
});
|
|
28346
|
-
|
|
28347
|
-
|
|
28348
|
-
|
|
28506
|
+
if (gridRef.current === grid2)
|
|
28507
|
+
setSaveNotice(
|
|
28508
|
+
result.ok ? result.notices.length > 0 ? `saved (${result.notices.join("; ")})` : null : result.error ?? "save failed"
|
|
28509
|
+
);
|
|
28349
28510
|
if (result.ok) {
|
|
28350
|
-
|
|
28351
|
-
|
|
28352
|
-
|
|
28511
|
+
if (result.bytes) {
|
|
28512
|
+
grid2.bytes = result.bytes;
|
|
28513
|
+
const baseline = await ingestSidecarBytes(
|
|
28514
|
+
result.bytes,
|
|
28515
|
+
dataExtensionOf(grid2.path),
|
|
28516
|
+
grid2.ingested.xlsx ? {
|
|
28517
|
+
sheet: grid2.ingested.xlsx.sheet,
|
|
28518
|
+
anchor: (await import("@bendyline/squisq-formats/xlsx")).formatCellRef(
|
|
28519
|
+
grid2.ingested.xlsx.anchorRow,
|
|
28520
|
+
grid2.ingested.xlsx.anchorCol
|
|
28521
|
+
),
|
|
28522
|
+
headerRow: grid2.ingested.xlsx.hasHeader
|
|
28523
|
+
} : { headerRow: grid2.ingested.csv?.hasHeader }
|
|
28524
|
+
);
|
|
28525
|
+
grid2.ingested = baseline;
|
|
28526
|
+
}
|
|
28527
|
+
grid2.formulaSession?.markSaved(formulaSnapshot, grid2.ingested.xlsx);
|
|
28528
|
+
if (gridRef.current === grid2) {
|
|
28529
|
+
setFormulaDirty(grid2.formulaSession?.dirtyCount ?? 0);
|
|
28530
|
+
onMediaSaved?.();
|
|
28531
|
+
}
|
|
28353
28532
|
}
|
|
28533
|
+
} catch (error) {
|
|
28534
|
+
if (gridRef.current === grid2)
|
|
28535
|
+
setSaveNotice(error instanceof Error ? error.message : String(error));
|
|
28354
28536
|
} finally {
|
|
28537
|
+
savingRef.current = false;
|
|
28355
28538
|
setSaving(false);
|
|
28356
28539
|
}
|
|
28357
|
-
}, [
|
|
28540
|
+
}, [href, onMediaSaved]);
|
|
28358
28541
|
if (!link || !href) return null;
|
|
28359
28542
|
const fileName = href.split("/").pop() ?? href;
|
|
28360
28543
|
const size = state.kind === "meta" || state.kind === "grid" ? state.size : null;
|
|
@@ -34764,8 +34947,8 @@ function playTimelineMediaAt(root, time) {
|
|
|
34764
34947
|
const end = Number(el.dataset.absEnd);
|
|
34765
34948
|
if (!Number.isFinite(start) || !Number.isFinite(end) || time < start || time >= end) return;
|
|
34766
34949
|
try {
|
|
34767
|
-
const
|
|
34768
|
-
|
|
34950
|
+
const pending2 = el.play();
|
|
34951
|
+
pending2?.catch(() => {
|
|
34769
34952
|
});
|
|
34770
34953
|
} catch {
|
|
34771
34954
|
}
|
|
@@ -37836,7 +38019,6 @@ import {
|
|
|
37836
38019
|
useState as useState68
|
|
37837
38020
|
} from "react";
|
|
37838
38021
|
import { parseTableViewState as parseTableViewState2 } from "@bendyline/squisq/table";
|
|
37839
|
-
import { DataGrid, TableStoreClient } from "@bendyline/squisq-grid-react";
|
|
37840
38022
|
|
|
37841
38023
|
// src/slideshow/slideshowDataSources.ts
|
|
37842
38024
|
import { isDataFilePath as isDataFilePath2, resolveTemplateName as resolveTemplateName3 } from "@bendyline/squisq/doc";
|
|
@@ -37938,8 +38120,18 @@ function SlideshowDataGridRenderer({
|
|
|
37938
38120
|
const source = sourceForBlock(sources, block);
|
|
37939
38121
|
const activeSourceKey = sourceKey(source);
|
|
37940
38122
|
const [remote, setRemote] = useState68({ key: "", status: "idle" });
|
|
38123
|
+
const [gridModule, setGridModule] = useState68(null);
|
|
37941
38124
|
useEffect59(() => {
|
|
37942
|
-
|
|
38125
|
+
let active = true;
|
|
38126
|
+
void loadGridModule().then((module) => {
|
|
38127
|
+
if (active) setGridModule(module);
|
|
38128
|
+
});
|
|
38129
|
+
return () => {
|
|
38130
|
+
active = false;
|
|
38131
|
+
};
|
|
38132
|
+
}, []);
|
|
38133
|
+
useEffect59(() => {
|
|
38134
|
+
if (!container || !source || !gridModule) {
|
|
37943
38135
|
setRemote({ key: activeSourceKey, status: "idle" });
|
|
37944
38136
|
return;
|
|
37945
38137
|
}
|
|
@@ -37955,7 +38147,8 @@ function SlideshowDataGridRenderer({
|
|
|
37955
38147
|
...source.anchor ? { anchor: source.anchor } : {},
|
|
37956
38148
|
...source.headerRow !== void 0 ? { headerRow: source.headerRow } : {}
|
|
37957
38149
|
});
|
|
37958
|
-
|
|
38150
|
+
if (cancelled) return;
|
|
38151
|
+
const provider2 = new gridModule.TableStoreClient(ingest2);
|
|
37959
38152
|
ownedProvider = provider2;
|
|
37960
38153
|
await provider2.describe();
|
|
37961
38154
|
if (cancelled) {
|
|
@@ -37973,7 +38166,7 @@ function SlideshowDataGridRenderer({
|
|
|
37973
38166
|
cancelled = true;
|
|
37974
38167
|
ownedProvider?.dispose();
|
|
37975
38168
|
};
|
|
37976
|
-
}, [activeSourceKey, container, mediaRevision]);
|
|
38169
|
+
}, [activeSourceKey, container, mediaRevision, gridModule]);
|
|
37977
38170
|
const currentRemote = remote.key === activeSourceKey ? remote : void 0;
|
|
37978
38171
|
const previewRows = useMemo52(
|
|
37979
38172
|
() => Array.isArray(layer.content.rows) ? layer.content.rows : [],
|
|
@@ -37985,8 +38178,8 @@ function SlideshowDataGridRenderer({
|
|
|
37985
38178
|
);
|
|
37986
38179
|
const useLocalGrid = (!source || currentRemote?.status === "error") && previewRows.length > (layer.content.maxVisibleRows ?? 10);
|
|
37987
38180
|
const localProvider = useMemo52(
|
|
37988
|
-
() => useLocalGrid ? new TableStoreClient({ headers: previewHeaders, cells: previewRows }) : null,
|
|
37989
|
-
[previewHeaders, previewRows, useLocalGrid]
|
|
38181
|
+
() => useLocalGrid && gridModule ? new gridModule.TableStoreClient({ headers: previewHeaders, cells: previewRows }) : null,
|
|
38182
|
+
[previewHeaders, previewRows, useLocalGrid, gridModule]
|
|
37990
38183
|
);
|
|
37991
38184
|
useEffect59(() => () => localProvider?.dispose(), [localProvider]);
|
|
37992
38185
|
const ready = currentRemote?.status === "ready" ? currentRemote : void 0;
|
|
@@ -37995,7 +38188,7 @@ function SlideshowDataGridRenderer({
|
|
|
37995
38188
|
const initialView = ready?.initialView ?? EMPTY_VIEW;
|
|
37996
38189
|
const [view, setView] = useState68(initialView);
|
|
37997
38190
|
useEffect59(() => setView(initialView), [initialView, provider]);
|
|
37998
|
-
if (!provider || !ingest) return /* @__PURE__ */ jsx77(Fragment22, { children: fallback });
|
|
38191
|
+
if (!provider || !ingest || !gridModule) return /* @__PURE__ */ jsx77(Fragment22, { children: fallback });
|
|
37999
38192
|
const { style } = layer.content;
|
|
38000
38193
|
const rowHeight = Math.max(32, style.fontSize * 2.1);
|
|
38001
38194
|
const footerHeight = Math.max(28, style.fontSize * 1.5);
|
|
@@ -38025,7 +38218,7 @@ function SlideshowDataGridRenderer({
|
|
|
38025
38218
|
"--squisq-grid-resize-handle": style.headerBackground
|
|
38026
38219
|
};
|
|
38027
38220
|
return /* @__PURE__ */ jsx77("div", { className: "squisq-slideshow-data-grid", style: gridStyle, children: /* @__PURE__ */ jsx77(
|
|
38028
|
-
DataGrid,
|
|
38221
|
+
gridModule.DataGrid,
|
|
38029
38222
|
{
|
|
38030
38223
|
provider,
|
|
38031
38224
|
view,
|
|
@@ -294,13 +294,19 @@ function useImageEditor(options) {
|
|
|
294
294
|
versioningAutoSaveIdleMs = 5e3,
|
|
295
295
|
persistDebounceMs = 500
|
|
296
296
|
} = options;
|
|
297
|
-
const
|
|
297
|
+
const target = useMemo(
|
|
298
|
+
() => ({ container, stateFilename, initialSrc, resourcePolicy }),
|
|
299
|
+
[container, stateFilename, initialSrc, resourcePolicy]
|
|
300
|
+
);
|
|
301
|
+
const [loadedTarget, setLoadedTarget] = useState2(null);
|
|
302
|
+
const [loadedState, dispatch] = useReducer(
|
|
298
303
|
(s, a) => {
|
|
299
304
|
if (s === null) return a.type === "load" ? initialImageEditorState(a.doc) : null;
|
|
300
305
|
return imageEditorReducer(s, a);
|
|
301
306
|
},
|
|
302
307
|
null
|
|
303
308
|
);
|
|
309
|
+
const state = loadedTarget === target ? loadedState : null;
|
|
304
310
|
const [ready, setReady] = useState2(false);
|
|
305
311
|
const [error, setError] = useState2(null);
|
|
306
312
|
const seededOnLoadRef = useRef2(false);
|
|
@@ -309,12 +315,15 @@ function useImageEditor(options) {
|
|
|
309
315
|
const controller = new AbortController();
|
|
310
316
|
setReady(false);
|
|
311
317
|
setError(null);
|
|
318
|
+
seededOnLoadRef.current = false;
|
|
312
319
|
(async () => {
|
|
313
320
|
try {
|
|
321
|
+
await writeQueueRef.current;
|
|
314
322
|
const existing = await readImageEditDoc(container, stateFilename);
|
|
315
323
|
if (cancelled) return;
|
|
316
324
|
if (existing) {
|
|
317
325
|
dispatch({ type: "load", doc: existing });
|
|
326
|
+
setLoadedTarget(target);
|
|
318
327
|
setReady(true);
|
|
319
328
|
return;
|
|
320
329
|
}
|
|
@@ -328,6 +337,7 @@ function useImageEditor(options) {
|
|
|
328
337
|
await writeImageEditDoc(container, seeded, stateFilename);
|
|
329
338
|
if (cancelled) return;
|
|
330
339
|
dispatch({ type: "load", doc: seeded });
|
|
340
|
+
setLoadedTarget(target);
|
|
331
341
|
setReady(true);
|
|
332
342
|
seededOnLoadRef.current = true;
|
|
333
343
|
} catch (err) {
|
|
@@ -340,33 +350,35 @@ function useImageEditor(options) {
|
|
|
340
350
|
cancelled = true;
|
|
341
351
|
controller.abort();
|
|
342
352
|
};
|
|
343
|
-
}, [container, stateFilename, initialSrc, resourcePolicy]);
|
|
353
|
+
}, [container, stateFilename, initialSrc, resourcePolicy, target]);
|
|
344
354
|
const persistTimerRef = useRef2(null);
|
|
345
355
|
const docRef = useRef2(null);
|
|
346
356
|
const dirtyRef = useRef2(false);
|
|
347
357
|
const revisionRef = useRef2(0);
|
|
348
358
|
const previousDocRef = useRef2(null);
|
|
349
|
-
const nextDoc =
|
|
359
|
+
const nextDoc = loadedState?.doc ?? null;
|
|
350
360
|
if (nextDoc !== previousDocRef.current) {
|
|
351
361
|
previousDocRef.current = nextDoc;
|
|
352
362
|
revisionRef.current += 1;
|
|
353
363
|
}
|
|
354
|
-
|
|
355
|
-
|
|
364
|
+
if (loadedTarget === target) {
|
|
365
|
+
docRef.current = nextDoc;
|
|
366
|
+
dirtyRef.current = state?.dirty ?? false;
|
|
367
|
+
}
|
|
356
368
|
const writeQueueRef = useRef2(Promise.resolve());
|
|
357
369
|
const persistTargetRef = useRef2({ container, stateFilename });
|
|
358
|
-
persistTargetRef.current =
|
|
370
|
+
if (loadedTarget === target) persistTargetRef.current = target;
|
|
359
371
|
const enqueueWrite = useCallback2(
|
|
360
372
|
(doc, revision, markClean) => {
|
|
361
373
|
const write = writeQueueRef.current.catch(() => void 0).then(() => writeImageEditDoc(container, doc, stateFilename));
|
|
362
374
|
writeQueueRef.current = write.catch(() => void 0);
|
|
363
375
|
return write.then(() => {
|
|
364
|
-
if (markClean && revision === revisionRef.current && docRef.current === doc) {
|
|
376
|
+
if (markClean && persistTargetRef.current === target && revision === revisionRef.current && docRef.current === doc) {
|
|
365
377
|
dispatch({ type: "mark-clean" });
|
|
366
378
|
}
|
|
367
379
|
});
|
|
368
380
|
},
|
|
369
|
-
[container, stateFilename]
|
|
381
|
+
[container, stateFilename, target]
|
|
370
382
|
);
|
|
371
383
|
useEffect2(() => {
|
|
372
384
|
if (!state?.dirty) return;
|
|
@@ -387,6 +399,7 @@ function useImageEditor(options) {
|
|
|
387
399
|
};
|
|
388
400
|
}, [state?.dirty, state?.doc, persistDebounceMs, enqueueWrite]);
|
|
389
401
|
const flush = useCallback2(async () => {
|
|
402
|
+
if (loadedTarget !== target) return;
|
|
390
403
|
const doc = docRef.current;
|
|
391
404
|
if (!doc) return;
|
|
392
405
|
if (persistTimerRef.current) {
|
|
@@ -394,14 +407,14 @@ function useImageEditor(options) {
|
|
|
394
407
|
persistTimerRef.current = null;
|
|
395
408
|
}
|
|
396
409
|
await enqueueWrite(doc, revisionRef.current, true);
|
|
397
|
-
}, [enqueueWrite]);
|
|
410
|
+
}, [enqueueWrite, loadedTarget, target]);
|
|
398
411
|
useEffect2(
|
|
399
412
|
() => () => {
|
|
400
413
|
if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
|
|
401
414
|
const doc = docRef.current;
|
|
402
415
|
if (!dirtyRef.current || !doc) return;
|
|
403
|
-
const
|
|
404
|
-
const write = writeQueueRef.current.catch(() => void 0).then(() => writeImageEditDoc(
|
|
416
|
+
const target2 = persistTargetRef.current;
|
|
417
|
+
const write = writeQueueRef.current.catch(() => void 0).then(() => writeImageEditDoc(target2.container, doc, target2.stateFilename));
|
|
405
418
|
writeQueueRef.current = write.catch((err) => {
|
|
406
419
|
console.warn(
|
|
407
420
|
"[squisq-editor] image-edit final persist failed:",
|
|
@@ -409,7 +422,7 @@ function useImageEditor(options) {
|
|
|
409
422
|
);
|
|
410
423
|
});
|
|
411
424
|
},
|
|
412
|
-
[]
|
|
425
|
+
[target]
|
|
413
426
|
);
|
|
414
427
|
const versioning = useMemo(
|
|
415
428
|
() => allowVersioning ? new ImageEditVersionManager(container, { stateFilename }) : null,
|
|
@@ -487,12 +500,14 @@ function useImageEditor(options) {
|
|
|
487
500
|
);
|
|
488
501
|
return {
|
|
489
502
|
state,
|
|
490
|
-
dispatch
|
|
503
|
+
dispatch: (action) => {
|
|
504
|
+
if (loadedTarget === target) dispatch(action);
|
|
505
|
+
},
|
|
491
506
|
flush,
|
|
492
507
|
resolveAssetUrl,
|
|
493
508
|
uploadAsset,
|
|
494
509
|
versioning,
|
|
495
|
-
ready,
|
|
510
|
+
ready: loadedTarget === target && ready,
|
|
496
511
|
error
|
|
497
512
|
};
|
|
498
513
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CodeContext, E as EditorHostMode, S as SceneTextChannel, a as CalcEngineFactory, b as CsvSourceMeta, X as XlsxSourceMeta, F as FormulaEditRecord } from './shell-
|
|
2
|
-
export { B as BlockRange, c as BlockSlice, d as BlockTagVisibility, e as CodeContextSection, f as CoverImageSaveOutput, D as DashboardImageSaveOutput, g as DocumentLinkCandidate, h as DocumentLinkProvider, i as EditorActions, j as EditorColorScheme, k as EditorContextMenuContext, l as EditorContextMenuItem, m as EditorContextMenuItemVisibility, n as EditorContextMenuProvider, o as EditorContextMenuProviderProps, p as EditorContextValue, q as EditorMode, r as EditorProvider, s as EditorProviderProps, t as EditorSelectionInfo, u as EditorShell, v as EditorShellProps, w as EditorState, x as EditorView, y as FormulaSessionOptions, I as ImageDisplayMode, z as IngestedSidecar, L as LayoutMode, M as MentionCandidate, A as MentionProvider, P as PreviewPanel, G as PreviewPanelProps, R as RawEditor, H as RawEditorProps, T as ThemeInheritance, V as ViewPreferences, W as WriteCanvasSettings, J as WysiwygEditor, K as WysiwygEditorProps, N as XLSX_LOCKED_REASON, O as XlsxFormulaSession, Q as createXlsxFormulaSession, U as getBlockSlices, Y as ingestSidecarBytes, Z as lineToOffset, _ as offsetToLine, $ as sliceIndexAtOffset, a0 as spliceBlock, a1 as useEditorContext, a2 as useEditorContextMenuItems } from './shell-
|
|
1
|
+
import { C as CodeContext, E as EditorHostMode, S as SceneTextChannel, a as CalcEngineFactory, b as CsvSourceMeta, X as XlsxSourceMeta, F as FormulaEditRecord } from './shell-X0EoQbbN.js';
|
|
2
|
+
export { B as BlockRange, c as BlockSlice, d as BlockTagVisibility, e as CodeContextSection, f as CoverImageSaveOutput, D as DashboardImageSaveOutput, g as DocumentLinkCandidate, h as DocumentLinkProvider, i as EditorActions, j as EditorColorScheme, k as EditorContextMenuContext, l as EditorContextMenuItem, m as EditorContextMenuItemVisibility, n as EditorContextMenuProvider, o as EditorContextMenuProviderProps, p as EditorContextValue, q as EditorMode, r as EditorProvider, s as EditorProviderProps, t as EditorSelectionInfo, u as EditorShell, v as EditorShellProps, w as EditorState, x as EditorView, y as FormulaSessionOptions, I as ImageDisplayMode, z as IngestedSidecar, L as LayoutMode, M as MentionCandidate, A as MentionProvider, P as PreviewPanel, G as PreviewPanelProps, R as RawEditor, H as RawEditorProps, T as ThemeInheritance, V as ViewPreferences, W as WriteCanvasSettings, J as WysiwygEditor, K as WysiwygEditorProps, N as XLSX_LOCKED_REASON, O as XlsxFormulaSession, Q as createXlsxFormulaSession, U as getBlockSlices, Y as ingestSidecarBytes, Z as lineToOffset, _ as offsetToLine, $ as sliceIndexAtOffset, a0 as spliceBlock, a1 as useEditorContext, a2 as useEditorContextMenuItems } from './shell-X0EoQbbN.js';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { ReactNode, CSSProperties, ReactElement } from 'react';
|
|
@@ -3022,17 +3022,16 @@ declare function viewStateFromBinding(binding: HeadingViewBinding, headers: read
|
|
|
3022
3022
|
* 1. BACKUP the original bytes first (`buildDataBackupPath`, pruned to the
|
|
3023
3023
|
* newest 3 per sidecar) — the only cross-save undo the grid offers.
|
|
3024
3024
|
* No container = no backup; the result says so rather than hiding it.
|
|
3025
|
-
* 2. Serialize from the
|
|
3026
|
-
*
|
|
3027
|
-
* not necessarily quoting style), sidestepping float-rendering drift.
|
|
3025
|
+
* 2. Serialize from the latest persisted bytes with a snapshot of this
|
|
3026
|
+
* region's journal, preserving saves made by other regions of the file.
|
|
3028
3027
|
* Formula neutralization applies ONLY to journal-edited, non-numeric
|
|
3029
3028
|
* cells (`serializeCsvRows` defaults `preserve`; blanket escaping would
|
|
3030
3029
|
* corrupt every negative number in a re-saved file).
|
|
3031
3030
|
* 3. `addMedia(samePath)` with FORK DETECTION: `addMedia` returns the path
|
|
3032
3031
|
* the host actually used, and a collision-renaming provider would
|
|
3033
3032
|
* silently fork the document's reference — surface it instead.
|
|
3034
|
-
* 4.
|
|
3035
|
-
*
|
|
3033
|
+
* 4. Acknowledge only the saved snapshot. The caller refreshes the formula
|
|
3034
|
+
* baseline and bumps mediaRevision so previews re-resolve on success.
|
|
3036
3035
|
*/
|
|
3037
3036
|
|
|
3038
3037
|
interface GridSaveResult {
|
|
@@ -3042,6 +3041,8 @@ interface GridSaveResult {
|
|
|
3042
3041
|
/** Human-readable caveats: fork detected, backup skipped, … */
|
|
3043
3042
|
notices: string[];
|
|
3044
3043
|
error?: string;
|
|
3044
|
+
/** Persisted baseline for subsequent saves, including other regions' edits. */
|
|
3045
|
+
bytes?: ArrayBuffer;
|
|
3045
3046
|
}
|
|
3046
3047
|
interface SaveCsvEditsOptions {
|
|
3047
3048
|
path: string;
|
package/dist/index.js
CHANGED
|
@@ -219,7 +219,7 @@ import {
|
|
|
219
219
|
useTreeViewData,
|
|
220
220
|
viewStateFromBinding,
|
|
221
221
|
writeHeadingViewState
|
|
222
|
-
} from "./chunk-
|
|
222
|
+
} from "./chunk-7B46MJNW.js";
|
|
223
223
|
import {
|
|
224
224
|
EditorProvider,
|
|
225
225
|
detectLanguageFromFileName,
|
|
@@ -249,7 +249,7 @@ import {
|
|
|
249
249
|
imageEditorReducer,
|
|
250
250
|
initialImageEditorState,
|
|
251
251
|
useImageEditor
|
|
252
|
-
} from "./chunk-
|
|
252
|
+
} from "./chunk-K4Z2GIX4.js";
|
|
253
253
|
import {
|
|
254
254
|
RecorderButton
|
|
255
255
|
} from "./chunk-DN6XFOY2.js";
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { d as BlockTagVisibility, g as DocumentLinkCandidate, h as DocumentLinkProvider, i as EditorActions, j as EditorColorScheme, k as EditorContextMenuContext, l as EditorContextMenuItem, m as EditorContextMenuItemVisibility, n as EditorContextMenuProvider, o as EditorContextMenuProviderProps, p as EditorContextValue, E as EditorHostMode, q as EditorMode, r as EditorProvider, s as EditorProviderProps, u as EditorShell, v as EditorShellProps, w as EditorState, x as EditorView, I as ImageDisplayMode, L as LayoutMode, M as MentionCandidate, A as MentionProvider, P as PreviewPanel, G as PreviewPanelProps, R as RawEditor, H as RawEditorProps, T as ThemeInheritance, V as ViewPreferences, J as WysiwygEditor, K as WysiwygEditorProps, a1 as useEditorContext, a2 as useEditorContextMenuItems } from '../shell-
|
|
1
|
+
export { d as BlockTagVisibility, g as DocumentLinkCandidate, h as DocumentLinkProvider, i as EditorActions, j as EditorColorScheme, k as EditorContextMenuContext, l as EditorContextMenuItem, m as EditorContextMenuItemVisibility, n as EditorContextMenuProvider, o as EditorContextMenuProviderProps, p as EditorContextValue, E as EditorHostMode, q as EditorMode, r as EditorProvider, s as EditorProviderProps, u as EditorShell, v as EditorShellProps, w as EditorState, x as EditorView, I as ImageDisplayMode, L as LayoutMode, M as MentionCandidate, A as MentionProvider, P as PreviewPanel, G as PreviewPanelProps, R as RawEditor, H as RawEditorProps, T as ThemeInheritance, V as ViewPreferences, J as WysiwygEditor, K as WysiwygEditorProps, a1 as useEditorContext, a2 as useEditorContextMenuItems } from '../shell-X0EoQbbN.js';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
3
|
import '@bendyline/squisq/table';
|
|
4
4
|
import '@bendyline/squisq-calc';
|
package/dist/shell/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
RawEditor,
|
|
6
6
|
WysiwygEditor,
|
|
7
7
|
useEditorContextMenuItems
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-7B46MJNW.js";
|
|
9
9
|
import {
|
|
10
10
|
EditorProvider,
|
|
11
11
|
useEditorContext
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import "../chunk-V4NBQF5C.js";
|
|
14
14
|
import "../chunk-ZDWZRVAY.js";
|
|
15
15
|
import "../chunk-EN5BYZOE.js";
|
|
16
|
-
import "../chunk-
|
|
16
|
+
import "../chunk-K4Z2GIX4.js";
|
|
17
17
|
import "../chunk-BJ3TXAIQ.js";
|
|
18
18
|
import "../chunk-GS7QWYFT.js";
|
|
19
19
|
import "../chunk-ENDRPV6T.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { TableCellValue, TableCellEdit } from '@bendyline/squisq/table';
|
|
3
3
|
import { CalcEngineConfig, CalcEngine } from '@bendyline/squisq-calc';
|
|
4
4
|
import { IngestTable } from '@bendyline/squisq-grid-react';
|
|
5
5
|
import { XlsxWorkbookGrids } from '@bendyline/squisq-formats/xlsx';
|
|
@@ -114,9 +114,13 @@ declare function ingestSidecarBytes(bytes: ArrayBuffer, ext: string, params: {
|
|
|
114
114
|
type CalcEngineFactory = (config: CalcEngineConfig) => Promise<CalcEngine>;
|
|
115
115
|
interface FormulaSessionOptions {
|
|
116
116
|
engineFactory?: CalcEngineFactory;
|
|
117
|
+
/** Caller-owned edits survive view remounts; scoped to the same journal. */
|
|
118
|
+
edits?: Map<string, FormulaEditRecord>;
|
|
117
119
|
}
|
|
118
120
|
interface FormulaEditRecord {
|
|
119
|
-
formula
|
|
121
|
+
/** null explicitly removes a formula. */
|
|
122
|
+
formula: string | null;
|
|
123
|
+
value?: TableCellValue;
|
|
120
124
|
/** Engine result, when scalar — persisted as the cached `<v>`. */
|
|
121
125
|
cachedValue?: number | boolean | string;
|
|
122
126
|
}
|
|
@@ -141,7 +145,9 @@ interface XlsxFormulaSession {
|
|
|
141
145
|
/** Revert to the loaded workbook; returns the display updates to re-apply. */
|
|
142
146
|
discard(): Promise<TableCellEdit[]>;
|
|
143
147
|
/** Forget the edits after a successful save (values stay current). */
|
|
144
|
-
markSaved(): void;
|
|
148
|
+
markSaved(snapshot?: ReadonlyMap<string, FormulaEditRecord>, baseline?: XlsxSourceMeta): void;
|
|
149
|
+
/** Wait for mutations already submitted to this session. */
|
|
150
|
+
flush(): Promise<void>;
|
|
145
151
|
dispose(): void;
|
|
146
152
|
}
|
|
147
153
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-editor-react",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.15",
|
|
4
4
|
"description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -98,12 +98,12 @@
|
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
100
|
"dependencies": {
|
|
101
|
-
"@bendyline/squisq": "2.11.
|
|
102
|
-
"@bendyline/squisq-calc": "2.11.
|
|
103
|
-
"@bendyline/squisq-formats": "2.6.
|
|
104
|
-
"@bendyline/squisq-grid-react": "2.11.
|
|
105
|
-
"@bendyline/squisq-react": "2.11.
|
|
106
|
-
"@bendyline/squisq-video-react": "2.4.
|
|
101
|
+
"@bendyline/squisq": "2.11.12",
|
|
102
|
+
"@bendyline/squisq-calc": "2.11.1",
|
|
103
|
+
"@bendyline/squisq-formats": "2.6.12",
|
|
104
|
+
"@bendyline/squisq-grid-react": "2.11.13",
|
|
105
|
+
"@bendyline/squisq-react": "2.11.14",
|
|
106
|
+
"@bendyline/squisq-video-react": "2.4.18",
|
|
107
107
|
"@fortawesome/fontawesome-free": "7.2.0",
|
|
108
108
|
"@tiptap/core": "2.27.3",
|
|
109
109
|
"@tiptap/extension-heading": "2.27.3",
|