| 641 | * @returns An object containing methods and getters for managing the workspace |
| 642 | */ |
| 643 | export const createWorkspaceStore = (workspaceProps?: WorkspaceProps): WorkspaceStore => { |
| 644 | const { verbose = false } = workspaceProps ?? {} |
| 645 | |
| 646 | const withMeasurementSync = <F extends () => unknown>( |
| 647 | name: string, |
| 648 | fn: ReturnType<F> extends Promise<unknown> ? never : F, |
| 649 | ): ReturnType<F> => (verbose ? measureSync(name, fn) : fn()) as ReturnType<F> |
| 650 | const withMeasurementAsync = <T>(name: string, fn: () => Promise<T>): Promise<T> => |
| 651 | verbose ? measureAsync(name, fn) : fn() |
| 652 | |
| 653 | /** |
| 654 | * Holds additional configuration options for each document in the workspace. |
| 655 | * |
| 656 | * This can include settings that can not be persisted between sessions (not JSON serializable) |
| 657 | */ |
| 658 | const extraDocumentConfigurations: ExtraDocumentConfigurations = {} |
| 659 | |
| 660 | /** |
| 661 | * Notifies all workspace plugins of a workspace state change event. |
| 662 | * |
| 663 | * This function iterates through all registered plugins (if any) and invokes |
| 664 | * their onWorkspaceStateChanges hook with the given event object. |
| 665 | * |
| 666 | * @param event - The workspace state change event to broadcast to plugins |
| 667 | */ |
| 668 | const fireWorkspaceChange = (event: WorkspaceStateChangeEvent) => { |
| 669 | workspaceProps?.plugins?.forEach((plugin) => plugin.hooks?.onWorkspaceStateChanges?.(event)) |
| 670 | } |
| 671 | |
| 672 | /** |
| 673 | * An object containing the reactive workspace state. |
| 674 | * |
| 675 | * Every change to the workspace, is tracked and broadcast to all registered plugins. |
| 676 | * allowing for change tracking. |
| 677 | * |
| 678 | * NOTE: |
| 679 | * The detect changes proxy is applied separately beacause the vue reactitvity proxy have to be the outer most proxy. |
| 680 | * If the order is reversed, Vue cannot properly track mutations, leading to lost reactivity and bugs. |
| 681 | * By wrapping the contents with the detect changes proxy first, and then passing the result to Vue's `reactive`, |
| 682 | * we ensure that Vue manages its reactivity as expected and our change detection hooks |
| 683 | * are also triggered reliably. |
| 684 | * Do not reverse this order‼️ |
| 685 | */ |
| 686 | const workspace = reactive<Workspace>( |
| 687 | createDetectChangesProxy( |
| 688 | { |
| 689 | ...workspaceProps?.meta, |
| 690 | documents: {}, |
| 691 | /** |
| 692 | * Returns the currently active document from the workspace. |
| 693 | * The active document is determined by the 'x-scalar-active-document' metadata field, |
| 694 | * falling back to the first document in the workspace if no active document is specified. |
| 695 | * |
| 696 | * @returns The active document or undefined if no document is found |
| 697 | */ |
| 698 | get activeDocument(): NonNullable<Workspace['activeDocument']> | undefined { |
| 699 | return workspace.documents[getActiveDocumentName()] |
| 700 | }, |