@a3s-lab/office 0.37.1 → 0.37.3

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.
@@ -0,0 +1,20 @@
1
+ import type { Editor } from '@tiptap/core';
2
+ /**
3
+ * Keeps controlled TipTap editors from publishing or reconciling content while
4
+ * the browser and ProseMirror still own an IME composition. The zero-delay
5
+ * settlement lets ProseMirror flush WebKit's pending DOM records first; the
6
+ * bounded retries cover runtimes that keep `view.composing` true briefly after
7
+ * `compositionend` without adding work to ordinary keyboard input.
8
+ */
9
+ export declare class ControlledEditorComposition {
10
+ private active;
11
+ private generation;
12
+ private settling;
13
+ private timer;
14
+ start(): void;
15
+ end(editor: Editor, onSettled: (editor: Editor) => void): void;
16
+ isBlocking(editor?: Editor | null): boolean;
17
+ destroy(): void;
18
+ private scheduleSettlement;
19
+ private cancelTimer;
20
+ }
@@ -35,5 +35,6 @@ export declare function spreadsheetContentWithSelection(content: WorkSpreadsheet
35
35
  export declare function spreadsheetContentWithSelections(content: WorkSpreadsheetContent, sheetId: string, selections: readonly Selection[]): WorkSpreadsheetContent;
36
36
  export declare function isSpreadsheetNativeTextUndoTarget(target: EventTarget | null): boolean;
37
37
  export declare function isSpreadsheetCellEditingTarget(target: EventTarget | null): boolean;
38
+ export declare function isSpreadsheetCellEditorTarget(target: EventTarget | null): boolean;
38
39
  export declare function spreadsheetFormulaBarSelectAllTarget(event: KeyboardEvent): HTMLElement | null;
39
40
  export declare function selectSpreadsheetFormulaBarContents(event: KeyboardEvent): boolean;
@@ -17,8 +17,9 @@ export interface SpreadsheetEditorProps {
17
17
  onAgentRequest?: (request: WorkEditorAgentRequest) => void | Promise<void>;
18
18
  }
19
19
  export declare function SpreadsheetEditor(props: SpreadsheetEditorProps): import("react").JSX.Element;
20
- export declare function focusSpreadsheetGrid(container: HTMLElement | null, { focusOrigin, forceInitial, }?: {
20
+ export declare function focusSpreadsheetGrid(container: HTMLElement | null, { focusOrigin, forceCellEditingExit, forceInitial, }?: {
21
21
  focusOrigin?: Element | null;
22
+ forceCellEditingExit?: boolean;
22
23
  forceInitial?: boolean;
23
24
  }): void;
24
25
  export declare function preserveSpreadsheetGridFocus(container: HTMLElement | null, target: EventTarget | null): void;
@@ -0,0 +1,15 @@
1
+ import { type SpreadsheetSortCustomList } from './spreadsheet-sort-custom-list';
2
+ export interface SpreadsheetSortCustomListManagementChange {
3
+ next: readonly string[] | null;
4
+ previous: readonly string[];
5
+ }
6
+ export interface SpreadsheetSortCustomListManagementResult {
7
+ changes: readonly SpreadsheetSortCustomListManagementChange[];
8
+ lists: readonly (readonly string[])[];
9
+ }
10
+ export declare function SpreadsheetSortCustomListManagerDialog({ customLists, restoreFocusTarget, onApply, onClose, }: {
11
+ customLists: readonly SpreadsheetSortCustomList[];
12
+ restoreFocusTarget: () => HTMLElement | null;
13
+ onApply: (value: SpreadsheetSortCustomListManagementResult) => void;
14
+ onClose: () => void;
15
+ }): import("react").JSX.Element;
@@ -1,9 +1,10 @@
1
1
  import { type SpreadsheetSortDialogSource, type SpreadsheetSortDialogValue } from './spreadsheet-sort';
2
2
  import { type SpreadsheetSortCustomList } from './spreadsheet-sort-custom-list';
3
- export declare function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRememberCustomList, onClose, }: {
3
+ export declare function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRememberCustomList, onUpdateCustomLists, onClose, }: {
4
4
  source: SpreadsheetSortDialogSource;
5
5
  restoreFocusTarget: () => HTMLElement | null;
6
6
  onApply: (value: SpreadsheetSortDialogValue) => boolean;
7
- onRememberCustomList?: (list: SpreadsheetSortCustomList) => SpreadsheetSortCustomList | void;
7
+ onRememberCustomList?: (list: SpreadsheetSortCustomList) => SpreadsheetSortCustomList | undefined;
8
+ onUpdateCustomLists?: (lists: readonly (readonly string[])[]) => readonly SpreadsheetSortCustomList[] | undefined;
8
9
  onClose: () => void;
9
10
  }): import("react").JSX.Element;
@@ -10,6 +10,8 @@ export interface UseDocumentReviewConflictsOptions {
10
10
  appliedSourceKeyRef: MutableValue<string>;
11
11
  artifactId?: string;
12
12
  content: WorkDocumentContent;
13
+ controlledUpdateRevision?: number;
14
+ deferControlledUpdates?: boolean;
13
15
  editor: Editor | null;
14
16
  editorInput: WorkDocumentEditorInput;
15
17
  normalizedContent: string;
@@ -17,7 +19,7 @@ export interface UseDocumentReviewConflictsOptions {
17
19
  publishedDocumentRef: MutableValue<Editor['state']['doc'] | null>;
18
20
  reconcileControlledUpdates?: boolean;
19
21
  }
20
- export declare function useDocumentReviewConflicts({ activeConflictsRef, appliedSourceKeyRef, artifactId, content, editor, editorInput, normalizedContent, onReviewConflict, publishedDocumentRef, reconcileControlledUpdates, }: UseDocumentReviewConflictsOptions): {
22
+ export declare function useDocumentReviewConflicts({ activeConflictsRef, appliedSourceKeyRef, artifactId, content, controlledUpdateRevision, deferControlledUpdates, editor, editorInput, normalizedContent, onReviewConflict, publishedDocumentRef, reconcileControlledUpdates, }: UseDocumentReviewConflictsOptions): {
21
23
  dismiss: () => void;
22
24
  visibleConflicts: WorkDocumentReviewConflict[];
23
25
  };
Binary file
package/dist/styles.css CHANGED
@@ -18122,6 +18122,13 @@ sub [data-office-character-position-half-points], sup [data-office-character-pos
18122
18122
  gap: 6px;
18123
18123
  }
18124
18124
 
18125
+ .work-spreadsheet-sort-toolbar-actions {
18126
+ align-items: center;
18127
+ gap: 6px;
18128
+ min-width: 0;
18129
+ display: flex;
18130
+ }
18131
+
18125
18132
  .work-spreadsheet-sort-toolbar .work-office-checkbox {
18126
18133
  min-height: 34px;
18127
18134
  padding: 6px 9px;
@@ -18314,6 +18321,96 @@ sub [data-office-character-position-half-points], sup [data-office-character-pos
18314
18321
  line-height: 1.45;
18315
18322
  }
18316
18323
 
18324
+ .work-spreadsheet-sort-custom-list-manager {
18325
+ --work-office-control-accent: var(--work-spreadsheet-accent, #159469);
18326
+ width: min(720px, 100%);
18327
+ }
18328
+
18329
+ .work-spreadsheet-sort-custom-list-manager-layout {
18330
+ grid-template-columns: minmax(220px, .85fr) minmax(280px, 1.15fr);
18331
+ gap: 14px;
18332
+ display: grid;
18333
+ }
18334
+
18335
+ .work-spreadsheet-sort-custom-list-manager-layout > section, .work-spreadsheet-sort-custom-list-manager-layout label {
18336
+ gap: 7px;
18337
+ min-width: 0;
18338
+ display: grid;
18339
+ }
18340
+
18341
+ .work-spreadsheet-sort-custom-list-manager-layout > section {
18342
+ align-content: start;
18343
+ }
18344
+
18345
+ .work-spreadsheet-sort-custom-list-manager-layout label > span {
18346
+ color: var(--a3s-muted);
18347
+ font-size: 10px;
18348
+ font-weight: 650;
18349
+ }
18350
+
18351
+ .work-spreadsheet-sort-custom-list-manager select, .work-spreadsheet-sort-custom-list-manager textarea {
18352
+ box-sizing: border-box;
18353
+ border: 1px solid var(--a3s-line-strong);
18354
+ background: var(--a3s-panel);
18355
+ width: 100%;
18356
+ min-width: 0;
18357
+ min-height: 252px;
18358
+ color: var(--a3s-ink);
18359
+ font: inherit;
18360
+ border-radius: 6px;
18361
+ padding: 8px;
18362
+ font-size: 11px;
18363
+ line-height: 1.5;
18364
+ }
18365
+
18366
+ .work-spreadsheet-sort-custom-list-manager textarea {
18367
+ resize: vertical;
18368
+ }
18369
+
18370
+ .work-spreadsheet-sort-custom-list-manager textarea[readonly] {
18371
+ background: var(--a3s-panel-soft);
18372
+ color: var(--a3s-muted);
18373
+ }
18374
+
18375
+ .work-spreadsheet-sort-custom-list-manager select:focus-visible, .work-spreadsheet-sort-custom-list-manager textarea:focus-visible {
18376
+ border-color: var(--work-spreadsheet-accent, #159469);
18377
+ outline: 2px solid
18378
+ color-mix(in srgb, var(--work-spreadsheet-accent, #159469) 24%, transparent);
18379
+ outline-offset: 1px;
18380
+ }
18381
+
18382
+ .work-spreadsheet-sort-custom-list-manager-layout section > p, .work-spreadsheet-sort-custom-list-manager-note {
18383
+ color: var(--a3s-muted);
18384
+ margin: 0;
18385
+ font-size: 10px;
18386
+ line-height: 1.45;
18387
+ }
18388
+
18389
+ .work-spreadsheet-sort-custom-list-manager-layout section > .work-spreadsheet-sort-custom-list-error {
18390
+ color: var(--a3s-danger, #b42318);
18391
+ }
18392
+
18393
+ .work-spreadsheet-sort-custom-list-manager-actions {
18394
+ justify-content: flex-end;
18395
+ align-items: center;
18396
+ gap: 6px;
18397
+ display: flex;
18398
+ }
18399
+
18400
+ .work-spreadsheet-sort-custom-list-manager-actions .ds-button {
18401
+ gap: 6px;
18402
+ }
18403
+
18404
+ .work-spreadsheet-sort-custom-list-manager-actions .ds-button[aria-label] {
18405
+ width: 34px;
18406
+ min-width: 34px;
18407
+ padding-inline: 0;
18408
+ }
18409
+
18410
+ .work-spreadsheet-sort-custom-list-manager-note {
18411
+ margin-block-start: 12px;
18412
+ }
18413
+
18317
18414
  .work-spreadsheet-sort-range-dialog {
18318
18415
  --work-office-control-accent: var(--work-spreadsheet-accent, #159469);
18319
18416
  width: min(500px, 100%);
@@ -18395,6 +18492,18 @@ sub [data-office-character-position-half-points], sup [data-office-character-pos
18395
18492
  align-items: stretch;
18396
18493
  }
18397
18494
 
18495
+ .work-spreadsheet-sort-toolbar-actions {
18496
+ flex-wrap: wrap;
18497
+ }
18498
+
18499
+ .work-spreadsheet-sort-custom-list-manager-layout {
18500
+ grid-template-columns: minmax(0, 1fr);
18501
+ }
18502
+
18503
+ .work-spreadsheet-sort-custom-list-manager select, .work-spreadsheet-sort-custom-list-manager textarea {
18504
+ min-height: 180px;
18505
+ }
18506
+
18398
18507
  .work-spreadsheet-sort-level {
18399
18508
  grid-template-columns: minmax(0, 1fr);
18400
18509
  }
@@ -644,6 +644,22 @@ The shell never searches visible labels, scrapes rendered text, or synthesizes
644
644
  clicks to infer product intent. This keeps localized UI copy independent from
645
645
  behavior and gives Worker/WASM operations a stable request contract.
646
646
 
647
+ ## Controlled rich-text IME boundary
648
+
649
+ Document body text, visual Markdown, and Presentation text share one internal
650
+ composition coordinator. `compositionstart` blocks host publication and
651
+ controlled-value reconciliation. `compositionend` schedules settlement after
652
+ the current task so ProseMirror can flush WebKit's pending DOM observer records;
653
+ bounded 20 ms retries cover runtimes that briefly retain `view.composing`.
654
+ Only the settled canonical value is published, and an authoritative host value
655
+ received during composition is reconciled afterward.
656
+
657
+ The coordinator creates no timers or extra renders for ordinary keyboard input.
658
+ Focused component tests cover controlled echo and mid-composition host
659
+ replacement. A dedicated Playwright WebKit gate exercises the browser event
660
+ ordering, asserts zero publications for Pinyin pre-edit text, one publication
661
+ for the committed Chinese value, and the same value after a controlled reopen.
662
+
647
663
  ## Markdown editing flow
648
664
 
649
665
  ```text
@@ -1455,14 +1471,19 @@ first/last placement. The
1455
1471
  normalization, duplicate and size guards, seven immutable month/weekday lists,
1456
1472
  and rank matching; the validated request clones custom entries so command
1457
1473
  execution has no dependency on mutable UI state. The browser adapters own a
1458
- bounded user-list registry and never writes preferences into controlled
1459
- workbook content. Without a store it remains scoped to the mounted session.
1474
+ bounded user-list registry and never write preferences into controlled workbook
1475
+ content. A dedicated manager stages create, edit, delete, and reorder operations
1476
+ against stable list identities, then publishes one normalized preference set.
1477
+ Built-ins remain read-only, no-op confirmation skips the storage boundary,
1478
+ edited active keys receive the replacement sequence, and deleted active keys
1479
+ fall back to ascending value order. Without a store the registry remains scoped
1480
+ to the mounted session.
1460
1481
  The optional typed `SpreadsheetSortCustomListStore` synchronously loads and
1461
1482
  saves canonical lists through a host-selected local backend. The provided
1462
1483
  `LocalStorageSpreadsheetSortCustomListStore` accepts an injected Storage
1463
1484
  object, uses a versioned key, and validates, deduplicates, and caps reads at 32
1464
- lists. Corrupt reads fail closed; a rejected write visibly downgrades the new
1465
- list to session scope. A stored list precedes an identical request-carried
1485
+ lists. Corrupt reads fail closed; a rejected write visibly downgrades the
1486
+ complete changed preference set to session scope. A stored list precedes an identical request-carried
1466
1487
  session copy so persistence identity remains stable. The separate
1467
1488
  `spreadsheet-sort-appearance` model snapshots direct native styles plus
1468
1489
  calculated conditional-format output, preserves no-fill/automatic identities,
@@ -1481,9 +1502,9 @@ One accepted operation crosses the Fortune boundary as one range write and one
1481
1502
  controlled Undo record. Table, worksheet-AutoFilter, hyperlink-map,
1482
1503
  imported-formula-metadata, and border-sidecar intersections fail closed because
1483
1504
  those models still own coordinates outside the cell matrix. Moving large sorts,
1484
- large aggregate/rank scans, remaining advanced filter predicates, full custom-list preference
1485
- management, and structural sidecar reconciliation into the Worker/WASM kernel
1486
- remains Stage 3 work.
1505
+ large aggregate/rank scans, remaining advanced filter predicates, and
1506
+ structural sidecar reconciliation into the Worker/WASM kernel remains Stage 3
1507
+ work.
1487
1508
  Direct host focus of the native grid now enters the same bounded focus observer
1488
1509
  used by ribbon commands. If a controlled workbook update replaces the focused
1489
1510
  Fortune overlay, focus transfers to its connected replacement; deliberate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a3s-lab/office",
3
- "version": "0.37.1",
3
+ "version": "0.37.3",
4
4
  "description": "Open-source collaborative browser editors for documents, Markdown, spreadsheets, presentations, and PDFs.",
5
5
  "keywords": [
6
6
  "office",
@@ -86,6 +86,7 @@
86
86
  "playground:bundle-check": "bun scripts/check-playground-bundle.ts",
87
87
  "playground:preview": "rspress preview -c website/rspress.config.ts --host 127.0.0.1 --port 4175",
88
88
  "playground:typecheck": "tsc --noEmit -p playground/tsconfig.json",
89
+ "playground:ime:webkit": "playwright test --config playwright.ime.config.ts",
89
90
  "playground:visual": "playwright test --config playwright.config.ts",
90
91
  "playground:visual:document-emphasis": "playwright test visual-tests/document-emphasis.functional.spec.ts --config playwright.config.ts",
91
92
  "playground:visual:document-index": "playwright test visual-tests/document-index.functional.spec.ts --config playwright.config.ts",