@a3s-lab/office 0.34.0 → 0.36.0

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/0~7048.js CHANGED
@@ -1462,6 +1462,7 @@ function validateSpreadsheetCalculationRequest(request) {
1462
1462
  if (!sheet.id.trim() || utf8ByteLength(sheet.id) > MAX_SPREADSHEET_IDENTIFIER_BYTES || !sheet.name.trim() || utf8ByteLength(sheet.name) > MAX_SPREADSHEET_IDENTIFIER_BYTES || sheetIds.has(sheet.id) || sheetNames.has(normalizedName)) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.sheet_invalid', 'Spreadsheet sheet IDs and names must be unique and non-empty.');
1463
1463
  sheetIds.add(sheet.id);
1464
1464
  sheetNames.add(normalizedName);
1465
+ validateSpreadsheetTables(sheet, request.sheets);
1465
1466
  const coordinates = new Set();
1466
1467
  for (const cell of sheet.cells){
1467
1468
  const key = `${cell.row}:${cell.column}`;
@@ -1473,6 +1474,68 @@ function validateSpreadsheetCalculationRequest(request) {
1473
1474
  }
1474
1475
  for (const target of request.targets ?? [])if (!sheetIds.has(target.sheetId) || !boundedSpreadsheetIndex(target.row, office_kernel_spreadsheet_protocol_OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) || !boundedSpreadsheetIndex(target.column, 16384)) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.target_invalid', 'Spreadsheet calculation targets must reference an existing, bounded cell.');
1475
1476
  }
1477
+ function validateSpreadsheetTables(sheet, sheets) {
1478
+ const tables = sheet.tables ?? [];
1479
+ const tableCount = sheets.reduce((count, candidate)=>count + (candidate.tables?.length ?? 0), 0);
1480
+ if (tableCount > 1024) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.table_limit_exceeded', 'A Spreadsheet calculation request may contain at most 1024 tables.');
1481
+ const aliases = new Map();
1482
+ for (const candidate of sheets)for (const [tableIndex, table] of (candidate.tables ?? []).entries()){
1483
+ const identity = `${candidate.id}\u0000${tableIndex}`;
1484
+ for (const alias of [
1485
+ table.name,
1486
+ table.displayName
1487
+ ]){
1488
+ if (!alias) continue;
1489
+ const normalized = alias.toLocaleLowerCase();
1490
+ const existing = aliases.get(normalized);
1491
+ if (existing && existing !== identity) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.table_invalid', `Spreadsheet table name '${alias}' is ambiguous.`);
1492
+ aliases.set(normalized, identity);
1493
+ }
1494
+ }
1495
+ const ranges = [];
1496
+ for (const table of tables){
1497
+ for (const [kind, value] of [
1498
+ [
1499
+ 'startRow',
1500
+ table.startRow
1501
+ ],
1502
+ [
1503
+ 'endRow',
1504
+ table.endRow
1505
+ ],
1506
+ [
1507
+ 'startColumn',
1508
+ table.startColumn
1509
+ ],
1510
+ [
1511
+ 'endColumn',
1512
+ table.endColumn
1513
+ ]
1514
+ ])if (!boundedSpreadsheetIndex(value, kind.endsWith('Row') ? office_kernel_spreadsheet_protocol_OFFICE_KERNEL_SPREADSHEET_MAX_ROWS : 16384)) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.table_invalid', `Spreadsheet table '${table.name}' has an out-of-bounds ${kind}.`);
1515
+ if (table.startRow > table.endRow || table.startColumn > table.endColumn || table.columns.length !== table.endColumn - table.startColumn + 1) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.table_invalid', `Spreadsheet table '${table.name}' has an invalid range or column count.`);
1516
+ validateTableName(table.name, 'name');
1517
+ if (void 0 !== table.displayName) validateTableName(table.displayName, 'displayName');
1518
+ const columnNames = new Set();
1519
+ for (const column of table.columns){
1520
+ validateTableName(column, 'column');
1521
+ const normalized = column.toLocaleLowerCase();
1522
+ if (columnNames.has(normalized)) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.table_invalid', `Spreadsheet table '${table.name}' contains duplicate column names.`);
1523
+ columnNames.add(normalized);
1524
+ }
1525
+ if (table.headerRow && table.totalsRow && table.startRow === table.endRow) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.table_invalid', `Spreadsheet table '${table.name}' cannot use header and totals rows in one row.`);
1526
+ for (const previous of ranges)if (table.startRow <= previous.endRow && table.endRow >= previous.startRow && table.startColumn <= previous.endColumn && table.endColumn >= previous.startColumn) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.table_invalid', `Spreadsheet tables '${previous.name}' and '${table.name}' overlap.`);
1527
+ ranges.push({
1528
+ startRow: table.startRow,
1529
+ endRow: table.endRow,
1530
+ startColumn: table.startColumn,
1531
+ endColumn: table.endColumn,
1532
+ name: table.name
1533
+ });
1534
+ }
1535
+ }
1536
+ function validateTableName(value, kind) {
1537
+ if (!value.trim() || utf8ByteLength(value) > MAX_SPREADSHEET_IDENTIFIER_BYTES) throw office_kernel_spreadsheet_fallback_validation_kernelError('office.kernel.spreadsheet.table_invalid', `Spreadsheet table ${kind} must contain 1-${MAX_SPREADSHEET_IDENTIFIER_BYTES} UTF-8 bytes.`);
1538
+ }
1476
1539
  function boundedSpreadsheetIndex(value, exclusiveMaximum) {
1477
1540
  return Number.isSafeInteger(value) && value >= 0 && value < exclusiveMaximum;
1478
1541
  }
@@ -1566,6 +1629,428 @@ function isParserErrorValue(value) {
1566
1629
  const withSuffix = '#DIV/0' === normalized ? '#DIV/0!' : normalized;
1567
1630
  return isOfficeKernelSpreadsheetError(withSuffix);
1568
1631
  }
1632
+ class SpreadsheetStructuredReferenceError extends Error {
1633
+ kind;
1634
+ constructor(kind, message){
1635
+ super(message);
1636
+ this.name = 'SpreadsheetStructuredReferenceError';
1637
+ this.kind = kind;
1638
+ }
1639
+ }
1640
+ function parseSpreadsheetStructuredReference(reference) {
1641
+ const open = reference.indexOf('[');
1642
+ if (open < 0) throw invalidReference(reference);
1643
+ const tableName = reference.slice(0, open) || void 0;
1644
+ const content = outerGroup(reference.slice(open));
1645
+ if (null === content) throw invalidReference(reference);
1646
+ const rows = {
1647
+ all: false,
1648
+ headers: false,
1649
+ data: false,
1650
+ totals: false,
1651
+ current: false
1652
+ };
1653
+ let firstColumn;
1654
+ let lastColumn;
1655
+ if (content.startsWith('@')) {
1656
+ rows.current = true;
1657
+ const column = parseCurrentColumn(content.slice(1), reference);
1658
+ firstColumn = column;
1659
+ lastColumn = column;
1660
+ } else if (content.startsWith('[')) [firstColumn, lastColumn] = parseNestedSelection(content, reference, rows);
1661
+ else {
1662
+ const item = tableItem(content);
1663
+ if (item) applyTableItem(rows, item, reference);
1664
+ else {
1665
+ const column = parsePlainColumn(content, reference);
1666
+ firstColumn = column;
1667
+ lastColumn = column;
1668
+ }
1669
+ }
1670
+ if (!rows.all && !rows.headers && !rows.data && !rows.totals && !rows.current) rows.data = true;
1671
+ return {
1672
+ tableName,
1673
+ firstColumn,
1674
+ lastColumn,
1675
+ rows
1676
+ };
1677
+ }
1678
+ class SpreadsheetStructuredReferenceCatalog {
1679
+ sheets;
1680
+ definitions = [];
1681
+ byName = new Map();
1682
+ bySheet = new Map();
1683
+ constructor(sheets){
1684
+ this.sheets = sheets;
1685
+ for (const sheet of sheets){
1686
+ const indexes = [];
1687
+ for (const table of sheet.tables ?? []){
1688
+ const definition = {
1689
+ ...table,
1690
+ sheetId: sheet.id,
1691
+ sheetName: sheet.name
1692
+ };
1693
+ const index = this.definitions.length;
1694
+ this.definitions.push(definition);
1695
+ indexes.push(index);
1696
+ for (const alias of [
1697
+ table.name,
1698
+ table.displayName
1699
+ ]){
1700
+ if (!alias) continue;
1701
+ const key = alias.toLocaleLowerCase();
1702
+ if (!this.byName.has(key)) this.byName.set(key, index);
1703
+ }
1704
+ }
1705
+ this.bySheet.set(sheet.id, indexes);
1706
+ }
1707
+ }
1708
+ resolve(qualifier, reference, currentSheet, currentColumn, currentRow) {
1709
+ const parsed = parseSpreadsheetStructuredReference(reference);
1710
+ const table = this.resolveTable(parsed, reference, currentSheet, currentColumn, currentRow);
1711
+ if (qualifier && table.sheetName.toLocaleLowerCase() !== qualifier.toLocaleLowerCase()) throw new SpreadsheetStructuredReferenceError('missing-table', `Spreadsheet table '${table.name}' is not on worksheet '${qualifier}'.`);
1712
+ if (parsed.rows.current && currentSheet.id !== table.sheetId) throw new SpreadsheetStructuredReferenceError('unsupported', `Structured reference #This Row requires the current formula cell to be on table '${table.name}'.`);
1713
+ const [startColumn, endColumn] = this.resolveColumns(table, parsed);
1714
+ return this.resolveRows(table, parsed.rows, currentRow).map(([startRow, endRow])=>({
1715
+ sheetId: table.sheetId,
1716
+ sheetName: table.sheetName,
1717
+ startRow,
1718
+ endRow,
1719
+ startColumn,
1720
+ endColumn
1721
+ }));
1722
+ }
1723
+ resolveTable(parsed, reference, currentSheet, currentColumn, currentRow) {
1724
+ if (parsed.tableName) {
1725
+ const index = this.byName.get(parsed.tableName.toLocaleLowerCase());
1726
+ if (void 0 === index) throw new SpreadsheetStructuredReferenceError('missing-table', `Spreadsheet table '${parsed.tableName}' does not exist.`);
1727
+ const table = this.definitions[index];
1728
+ if (!table) throw invalidReference(reference);
1729
+ return table;
1730
+ }
1731
+ const matching = (this.bySheet.get(currentSheet.id) ?? []).map((index)=>this.definitions[index]).filter((table)=>void 0 !== table && currentRow >= table.startRow && currentRow <= table.endRow && currentColumn >= table.startColumn && currentColumn <= table.endColumn);
1732
+ if (!matching.length) throw new SpreadsheetStructuredReferenceError('missing-table', `Table-local structured reference '${reference}' requires the current formula cell to be inside a Spreadsheet table.`);
1733
+ if (matching.length > 1) throw new SpreadsheetStructuredReferenceError('unsupported', `Table-local structured reference '${reference}' is ambiguous at the current formula cell.`);
1734
+ return matching[0];
1735
+ }
1736
+ resolveColumns(table, parsed) {
1737
+ let first = 0;
1738
+ let last = table.columns.length - 1;
1739
+ if (void 0 !== parsed.firstColumn && void 0 !== parsed.lastColumn) {
1740
+ first = table.columns.findIndex((column)=>column.toLocaleLowerCase() === parsed.firstColumn.toLocaleLowerCase());
1741
+ last = table.columns.findIndex((column)=>column.toLocaleLowerCase() === parsed.lastColumn.toLocaleLowerCase());
1742
+ if (first < 0) throw new SpreadsheetStructuredReferenceError('missing-column', `Spreadsheet table '${table.name}' has no column '${parsed.firstColumn}'.`);
1743
+ if (last < 0) throw new SpreadsheetStructuredReferenceError('missing-column', `Spreadsheet table '${table.name}' has no column '${parsed.lastColumn}'.`);
1744
+ if (first > last) throw new SpreadsheetStructuredReferenceError('unsupported', `Structured-reference column range '${parsed.firstColumn}:${parsed.lastColumn}' is reversed.`);
1745
+ }
1746
+ if (first < 0 || last < first || table.startColumn + last > table.endColumn) throw invalidReference(table.name);
1747
+ return [
1748
+ table.startColumn + first,
1749
+ table.startColumn + last
1750
+ ];
1751
+ }
1752
+ resolveRows(table, rows, currentRow) {
1753
+ const selected = [];
1754
+ if (rows.all) selected.push([
1755
+ table.startRow,
1756
+ table.endRow
1757
+ ]);
1758
+ if (rows.headers) {
1759
+ if (!table.headerRow) throw missingRows(table, '#Headers');
1760
+ selected.push([
1761
+ table.startRow,
1762
+ table.startRow
1763
+ ]);
1764
+ }
1765
+ if (rows.data) selected.push(this.dataRows(table));
1766
+ if (rows.totals) {
1767
+ if (!table.totalsRow) throw missingRows(table, '#Totals');
1768
+ selected.push([
1769
+ table.endRow,
1770
+ table.endRow
1771
+ ]);
1772
+ }
1773
+ if (rows.current) {
1774
+ const [start, end] = this.dataRows(table);
1775
+ if (currentRow < start || currentRow > end) throw new SpreadsheetStructuredReferenceError('unsupported', `Structured reference #This Row requires the current formula row to be inside table '${table.name}'.`);
1776
+ selected.push([
1777
+ currentRow,
1778
+ currentRow
1779
+ ]);
1780
+ }
1781
+ selected.sort((left, right)=>left[0] - right[0]);
1782
+ const merged = [];
1783
+ for (const [start, end] of selected){
1784
+ const previous = merged.at(-1);
1785
+ if (previous && start <= previous[1] + 1) previous[1] = Math.max(previous[1], end);
1786
+ else merged.push([
1787
+ start,
1788
+ end
1789
+ ]);
1790
+ }
1791
+ if (!merged.length) throw new SpreadsheetStructuredReferenceError('unsupported', 'Structured reference selects no table rows.');
1792
+ return merged;
1793
+ }
1794
+ dataRows(table) {
1795
+ const start = table.startRow + (table.headerRow ? 1 : 0);
1796
+ const end = table.endRow - (table.totalsRow ? 1 : 0);
1797
+ if (start > end) throw new SpreadsheetStructuredReferenceError('unsupported', `Spreadsheet table '${table.name}' has no data rows.`);
1798
+ return [
1799
+ start,
1800
+ end
1801
+ ];
1802
+ }
1803
+ }
1804
+ function expandSpreadsheetStructuredReferences(formula, catalog, currentSheet, currentRow, currentColumn) {
1805
+ const hasEquals = formula.startsWith('=');
1806
+ const source = hasEquals ? formula.slice(1) : formula;
1807
+ let output = '';
1808
+ let cursor = 0;
1809
+ while(cursor < source.length){
1810
+ const character = source[cursor] ?? '';
1811
+ if ('"' === character) {
1812
+ const end = quotedStringEnd(source, cursor);
1813
+ output += source.slice(cursor, end);
1814
+ cursor = end;
1815
+ continue;
1816
+ }
1817
+ const token = scanStructuredReference(source, cursor);
1818
+ if (!token) {
1819
+ output += character;
1820
+ cursor += 1;
1821
+ continue;
1822
+ }
1823
+ const areas = catalog.resolve(token.qualifier, token.reference, currentSheet, currentColumn, currentRow);
1824
+ if (areas.length > 1) throw new SpreadsheetStructuredReferenceError('unsupported', `Structured reference '${token.reference}' resolves to disjoint row areas; the JavaScript fallback requires a contiguous range.`);
1825
+ const ranges = areas.map((area)=>{
1826
+ const prefix = area.sheetId === currentSheet.id ? '' : `${quoteSheetName(area.sheetName)}!`;
1827
+ const start = cellAddress(area.startRow, area.startColumn);
1828
+ const end = cellAddress(area.endRow, area.endColumn);
1829
+ return `${prefix}${start}${start === end ? '' : `:${end}`}`;
1830
+ });
1831
+ output += 1 === ranges.length ? ranges[0] : `(${ranges.join(',')})`;
1832
+ cursor = token.end;
1833
+ }
1834
+ return hasEquals ? `=${output}` : output;
1835
+ }
1836
+ function scanStructuredReference(source, start) {
1837
+ const qualified = scanQualifier(source, start);
1838
+ let cursor = qualified?.end ?? start;
1839
+ const qualifier = qualified?.name;
1840
+ if ('[' === source[cursor]) {
1841
+ const end = matchingBracket(source, cursor);
1842
+ if (null === end) return null;
1843
+ const content = source.slice(cursor + 1, end - 1);
1844
+ if (!content.startsWith('@') && !content.startsWith('#') && !content.startsWith('[')) return null;
1845
+ return {
1846
+ end,
1847
+ qualifier,
1848
+ reference: source.slice(cursor, end)
1849
+ };
1850
+ }
1851
+ const nameStart = cursor;
1852
+ if (!isNameStart(source[cursor] ?? '')) return null;
1853
+ cursor += 1;
1854
+ while(cursor < source.length && isNameContinue(source[cursor]))cursor += 1;
1855
+ if ('[' !== source[cursor]) return null;
1856
+ const end = matchingBracket(source, cursor);
1857
+ if (null === end) throw invalidReference(source.slice(nameStart, source.length));
1858
+ return {
1859
+ end,
1860
+ qualifier,
1861
+ reference: source.slice(nameStart, end)
1862
+ };
1863
+ }
1864
+ function scanQualifier(source, start) {
1865
+ if ("'" === source[start]) {
1866
+ let cursor = start + 1;
1867
+ let decoded = '';
1868
+ while(cursor < source.length){
1869
+ const character = source[cursor];
1870
+ if ("'" === character) {
1871
+ if ("'" === source[cursor + 1]) {
1872
+ decoded += "'";
1873
+ cursor += 2;
1874
+ continue;
1875
+ }
1876
+ if ('!' === source[cursor + 1]) return {
1877
+ name: decoded,
1878
+ end: cursor + 2
1879
+ };
1880
+ break;
1881
+ }
1882
+ decoded += character;
1883
+ cursor += 1;
1884
+ }
1885
+ return null;
1886
+ }
1887
+ let cursor = start;
1888
+ while(cursor < source.length && isQualifierCharacter(source[cursor]))cursor += 1;
1889
+ if ('!' !== source[cursor] || cursor === start) return null;
1890
+ return {
1891
+ name: source.slice(start, cursor),
1892
+ end: cursor + 1
1893
+ };
1894
+ }
1895
+ function matchingBracket(source, start) {
1896
+ let depth = 0;
1897
+ for(let cursor = start; cursor < source.length; cursor += 1){
1898
+ const character = source[cursor];
1899
+ if ("'" === character) {
1900
+ cursor += "'" === source[cursor + 1] ? 1 : 0;
1901
+ continue;
1902
+ }
1903
+ if ('[' === character) depth += 1;
1904
+ else if (']' === character) {
1905
+ depth -= 1;
1906
+ if (0 === depth) return cursor + 1;
1907
+ if (depth < 0) break;
1908
+ }
1909
+ }
1910
+ return null;
1911
+ }
1912
+ function outerGroup(value) {
1913
+ if (!value.startsWith('[')) return null;
1914
+ const end = matchingBracket(value, 0);
1915
+ return end === value.length ? value.slice(1, -1) : null;
1916
+ }
1917
+ function bracketAtom(value) {
1918
+ if (!value.startsWith('[')) return null;
1919
+ const end = matchingBracket(value, 0);
1920
+ return null === end ? null : {
1921
+ atom: value.slice(1, end - 1),
1922
+ consumed: end
1923
+ };
1924
+ }
1925
+ function parseCurrentColumn(value, reference) {
1926
+ if (value.startsWith('[')) {
1927
+ const atom = bracketAtom(value);
1928
+ if (!atom || atom.consumed !== value.length) throw invalidReference(reference);
1929
+ const column = decodeAtom(atom.atom);
1930
+ if (!column) throw invalidReference(reference);
1931
+ return column;
1932
+ }
1933
+ return parsePlainColumn(value, reference);
1934
+ }
1935
+ function parseNestedSelection(content, reference, rows) {
1936
+ const atoms = [];
1937
+ const separators = [];
1938
+ let cursor = 0;
1939
+ while(cursor < content.length){
1940
+ const atom = bracketAtom(content.slice(cursor));
1941
+ if (!atom) throw invalidReference(reference);
1942
+ atoms.push(atom.atom);
1943
+ cursor += atom.consumed;
1944
+ if (cursor === content.length) break;
1945
+ const separator = content[cursor];
1946
+ if (',' !== separator && ':' !== separator) throw invalidReference(reference);
1947
+ separators.push(separator);
1948
+ cursor += 1;
1949
+ }
1950
+ const columns = [];
1951
+ atoms.forEach((atom, index)=>{
1952
+ const item = tableItem(atom);
1953
+ if (item) applyTableItem(rows, item, reference);
1954
+ else {
1955
+ const column = decodeAtom(atom);
1956
+ if (!column) throw invalidReference(reference);
1957
+ columns.push({
1958
+ index,
1959
+ name: column
1960
+ });
1961
+ }
1962
+ });
1963
+ if (0 === columns.length) {
1964
+ if (separators.includes(':')) throw invalidReference(reference);
1965
+ return [
1966
+ void 0,
1967
+ void 0
1968
+ ];
1969
+ }
1970
+ if (1 === columns.length) {
1971
+ if (separators.includes(':')) throw invalidReference(reference);
1972
+ return [
1973
+ columns[0].name,
1974
+ columns[0].name
1975
+ ];
1976
+ }
1977
+ const first = columns[0];
1978
+ const last = columns[1];
1979
+ if (2 === columns.length && last.index === first.index + 1 && ':' === separators[first.index] && separators.every((separator, index)=>index === first.index || ',' === separator)) return [
1980
+ first.name,
1981
+ last.name
1982
+ ];
1983
+ throw new SpreadsheetStructuredReferenceError('unsupported', 'Disjoint structured-reference columns are not supported.');
1984
+ }
1985
+ function tableItem(value) {
1986
+ const normalized = value.toLocaleLowerCase();
1987
+ if ('#all' === normalized) return 'all';
1988
+ if ('#headers' === normalized) return 'headers';
1989
+ if ('#data' === normalized) return 'data';
1990
+ if ('#totals' === normalized) return 'totals';
1991
+ if ('#this row' === normalized) return 'current';
1992
+ }
1993
+ function applyTableItem(rows, item, reference) {
1994
+ rows['current' === item ? 'current' : item] = true;
1995
+ if (rows.current && (rows.all || rows.headers || rows.data || rows.totals)) throw new SpreadsheetStructuredReferenceError('unsupported', `Structured reference '${reference}' cannot combine #This Row with another item.`);
1996
+ }
1997
+ function parsePlainColumn(value, reference) {
1998
+ if (!value || /[[\],:]/u.test(value)) throw invalidReference(reference);
1999
+ const column = decodeAtom(value);
2000
+ if (!column) throw invalidReference(reference);
2001
+ return column;
2002
+ }
2003
+ function decodeAtom(value) {
2004
+ let output = '';
2005
+ for(let cursor = 0; cursor < value.length; cursor += 1){
2006
+ const character = value[cursor];
2007
+ if ("'" === character) {
2008
+ const escaped = value[cursor + 1];
2009
+ if (void 0 === escaped) throw invalidReference(value);
2010
+ output += escaped;
2011
+ cursor += 1;
2012
+ } else output += character;
2013
+ }
2014
+ return output;
2015
+ }
2016
+ function missingRows(table, item) {
2017
+ return new SpreadsheetStructuredReferenceError('unsupported', `Structured reference ${item} requires table '${table.name}' to contain that row.`);
2018
+ }
2019
+ function invalidReference(reference) {
2020
+ return new SpreadsheetStructuredReferenceError('invalid', `Structured reference '${reference}' is not in a supported canonical form.`);
2021
+ }
2022
+ function quotedStringEnd(source, start) {
2023
+ for(let cursor = start + 1; cursor < source.length; cursor += 1)if ('"' === source[cursor]) {
2024
+ if ('"' === source[cursor + 1]) {
2025
+ cursor += 1;
2026
+ continue;
2027
+ }
2028
+ return cursor + 1;
2029
+ }
2030
+ return source.length;
2031
+ }
2032
+ function cellAddress(row, column) {
2033
+ let value = column + 1;
2034
+ let label = '';
2035
+ while(value > 0){
2036
+ value -= 1;
2037
+ label = String.fromCharCode(65 + value % 26) + label;
2038
+ value = Math.floor(value / 26);
2039
+ }
2040
+ return `${label}${row + 1}`;
2041
+ }
2042
+ function quoteSheetName(name) {
2043
+ return /^[A-Za-z_][A-Za-z0-9_.]*$/u.test(name) ? name : `'${name.replaceAll("'", "''")}'`;
2044
+ }
2045
+ function isNameStart(value) {
2046
+ return /^[A-Za-z_\\?]$/u.test(value);
2047
+ }
2048
+ function isNameContinue(value) {
2049
+ return /^[A-Za-z0-9_.\\?]$/u.test(value);
2050
+ }
2051
+ function isQualifierCharacter(value) {
2052
+ return /^[A-Za-z0-9_.\\?[\]:$]$/u.test(value);
2053
+ }
1569
2054
  async function calculateSpreadsheetInJavaScript(request) {
1570
2055
  validateSpreadsheetCalculationRequest(request);
1571
2056
  const formulaParser = await import("@fortune-sheet/formula-parser");
@@ -1581,9 +2066,11 @@ class JavaScriptSpreadsheetEvaluator {
1581
2066
  stack = [];
1582
2067
  calculationOrder = [];
1583
2068
  issues = [];
2069
+ tableCatalog;
1584
2070
  constructor(request, formulaParser){
1585
2071
  this.request = request;
1586
2072
  this.formulaParser = formulaParser;
2073
+ this.tableCatalog = new SpreadsheetStructuredReferenceCatalog(request.sheets);
1587
2074
  for (const sheet of request.sheets){
1588
2075
  this.sheetsByName.set(sheet.name.toLowerCase(), sheet);
1589
2076
  for (const cell of sheet.cells)this.cells.set(cellKey(sheet.id, cell.row, cell.column), {
@@ -1654,6 +2141,13 @@ class JavaScriptSpreadsheetEvaluator {
1654
2141
  }
1655
2142
  evaluateFormula(coordinate, indexed) {
1656
2143
  const formula = indexed.cell.formula ?? '';
2144
+ let expandedFormula;
2145
+ try {
2146
+ expandedFormula = expandSpreadsheetStructuredReferences(formula, this.tableCatalog, indexed.sheet, coordinate.row, coordinate.column);
2147
+ } catch (error) {
2148
+ const message = error instanceof SpreadsheetStructuredReferenceError ? error.message : 'Structured reference expansion failed.';
2149
+ return failedEvaluation(indexed.cell.value, calculationIssue(coordinate, 'office.kernel.spreadsheet.formula_unsupported', message));
2150
+ }
1657
2151
  const parser = new this.formulaParser.Parser();
1658
2152
  let unresolvedDependency = false;
1659
2153
  let unsupportedReference = false;
@@ -1687,7 +2181,7 @@ class JavaScriptSpreadsheetEvaluator {
1687
2181
  return true;
1688
2182
  }));
1689
2183
  });
1690
- const parsed = parser.parse(normalizeFormulaForFortuneParser(formula), {
2184
+ const parsed = parser.parse(normalizeFormulaForFortuneParser(expandedFormula), {
1691
2185
  sheetId: indexed.sheet.id
1692
2186
  });
1693
2187
  if (unsupportedFunction) return failedEvaluation(indexed.cell.value, calculationIssue(coordinate, 'office.kernel.spreadsheet.formula_unsupported', `Formula function '${unsupportedFunction}' is not supported.`));
@@ -1711,7 +2205,8 @@ class JavaScriptSpreadsheetEvaluator {
1711
2205
  }
1712
2206
  rangeValues(currentSheet, start, end, onUnresolvedDependency, onUnsupportedReference, reserveRangeCells) {
1713
2207
  const startCoordinate = this.resolveCoordinate(currentSheet, start);
1714
- const endCoordinate = this.resolveCoordinate(currentSheet, end);
2208
+ const rangeSheet = start.sheetName ? this.sheetsByName.get(start.sheetName.toLowerCase()) : currentSheet;
2209
+ const endCoordinate = rangeSheet ? this.resolveCoordinate(rangeSheet, end) : null;
1715
2210
  if (!startCoordinate || !endCoordinate || startCoordinate.sheetId !== endCoordinate.sheetId) {
1716
2211
  onUnsupportedReference();
1717
2212
  return [];
@@ -2425,6 +2425,9 @@ function PlaceholderButtons({ onAdd }) {
2425
2425
  });
2426
2426
  }
2427
2427
  const PRESENTATION_WORKSPACE_FOCUS_RETRY_FRAMES = 6;
2428
+ function presentationInitialEditingElementId(elements) {
2429
+ return elements.find((element)=>element.placeholder?.type === 'title' && presentationElementCanEditContent(element) && !element.text.trim() && !element.textRuns?.some((run)=>run.text.trim()))?.id ?? null;
2430
+ }
2428
2431
  function restorePresentationWorkspaceFocus(root, getState, focusOrigin = document.activeElement) {
2429
2432
  if (!root) return;
2430
2433
  const commandTrigger = focusOrigin;
@@ -4356,6 +4359,7 @@ function PresentationTextEditor({ autoFocus = false, element, onChange, onEditor
4356
4359
  const onSelectionChangeRef = useRef(onSelectionChange);
4357
4360
  const appliedSignatureRef = useRef(presentationTextElementSignature(element));
4358
4361
  const initialContentRef = useRef(presentationTextElementHtml(element));
4362
+ const initialFocusTargetRef = useRef("u" > typeof document && document.activeElement instanceof HTMLElement ? document.activeElement : null);
4359
4363
  elementRef.current = element;
4360
4364
  onChangeRef.current = onChange;
4361
4365
  onEditorChangeRef.current = onEditorChange;
@@ -4410,7 +4414,8 @@ function PresentationTextEditor({ autoFocus = false, element, onChange, onEditor
4410
4414
  useEffect(()=>{
4411
4415
  if (!autoFocus || !editor || editor.isDestroyed) return;
4412
4416
  const frame = window.requestAnimationFrame(()=>{
4413
- if (!editor.isDestroyed) editor.commands.focus('end');
4417
+ if (editor.isDestroyed || !presentationFocusOwnerIsUnchanged(initialFocusTargetRef.current)) return;
4418
+ editor.commands.focus('end');
4414
4419
  });
4415
4420
  return ()=>window.cancelAnimationFrame(frame);
4416
4421
  }, [
@@ -4438,6 +4443,12 @@ function PresentationTextEditor({ autoFocus = false, element, onChange, onEditor
4438
4443
  style: presentationTextBaseStyle(element)
4439
4444
  });
4440
4445
  }
4446
+ function presentationFocusOwnerIsUnchanged(initialFocusTarget) {
4447
+ if ("u" < typeof document) return true;
4448
+ const currentFocusTarget = document.activeElement;
4449
+ if (!currentFocusTarget || currentFocusTarget === document.body) return true;
4450
+ return !initialFocusTarget || currentFocusTarget === initialFocusTarget;
4451
+ }
4441
4452
  function createPresentationTextEditorExtensions(placeholder = '输入文字') {
4442
4453
  return [
4443
4454
  starter_kit.configure({
@@ -7881,9 +7892,12 @@ function usePresentationReviewCommands({ content, onChange, onClearSelection, on
7881
7892
  function activePresentationReviewInvoker() {
7882
7893
  return "u" > typeof document && document.activeElement instanceof HTMLElement ? document.activeElement : null;
7883
7894
  }
7884
- function usePresentationSelection(elements) {
7885
- const [selectedElementIds, setSelectedElementIds] = useState([]);
7886
- const [editingElementId, setEditingElementId] = useState(null);
7895
+ function usePresentationSelection(elements, initialEditingElementId = null) {
7896
+ const initialEditingElementExists = Boolean(initialEditingElementId && elements.some((element)=>element.id === initialEditingElementId));
7897
+ const [selectedElementIds, setSelectedElementIds] = useState(()=>initialEditingElementExists && initialEditingElementId ? selectionWithPendingIds(elements, [
7898
+ initialEditingElementId
7899
+ ]) : []);
7900
+ const [editingElementId, setEditingElementId] = useState(()=>initialEditingElementExists ? initialEditingElementId : null);
7887
7901
  const clear = useCallback(()=>{
7888
7902
  setSelectedElementIds([]);
7889
7903
  setEditingElementId(null);
@@ -8482,7 +8496,7 @@ function PresentationEditingSurface({ initialSlide, autoFocus = true, collaborat
8482
8496
  const selectedLayout = designContent.layouts?.find((layout)=>layout.id === selectedSlide?.layoutId) ?? designContent.layouts?.[0];
8483
8497
  const selectedMaster = designContent.masters?.find((master)=>master.id === selectedLayout?.masterId) ?? designContent.masters?.[0];
8484
8498
  const activeElements = 'layout' === designMode ? selectedLayout?.elements ?? [] : 'master' === designMode ? selectedMaster?.elements ?? [] : selectedSlide?.elements ?? [];
8485
- const selection = usePresentationSelection(activeElements);
8499
+ const selection = usePresentationSelection(activeElements, autoFocus ? presentationInitialEditingElementId(activeElements) : null);
8486
8500
  const presentationPresenceLocation = useMemo(()=>({
8487
8501
  kind: 'presentation',
8488
8502
  slideId: selectedSlide.id,