@platforma-sdk/ui-vue 1.66.1 → 1.66.2

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.
Files changed (41) hide show
  1. package/.turbo/turbo-build.log +16 -14
  2. package/.turbo/turbo-formatter$colon$check.log +2 -2
  3. package/.turbo/turbo-linter$colon$check.log +2 -2
  4. package/.turbo/turbo-types$colon$check.log +1 -1
  5. package/CHANGELOG.md +11 -0
  6. package/dist/components/PlAdvancedFilter/FilterEditor.vue2.js +20 -20
  7. package/dist/components/PlAgColumnHeader/PlAgColumnHeader.js.map +1 -1
  8. package/dist/components/PlAgColumnHeader/PlAgColumnHeader.vue.d.ts.map +1 -1
  9. package/dist/components/PlAgColumnHeader/PlAgColumnHeader.vue2.js +43 -33
  10. package/dist/components/PlAgColumnHeader/PlAgColumnHeader.vue2.js.map +1 -1
  11. package/dist/components/PlAgColumnHeader/types.d.ts +1 -0
  12. package/dist/components/PlAgColumnHeader/types.d.ts.map +1 -1
  13. package/dist/components/PlAgDataTable/compositions/useFilterableColumns.js +5 -5
  14. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
  15. package/dist/components/PlAgDataTable/sources/table-source-v2.js +10 -11
  16. package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
  17. package/dist/components/PlAgDataTable/sources/table-state-v2.js +13 -13
  18. package/dist/components/PlAnnotations/components/PlAnnotations.vue2.js.map +1 -1
  19. package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js +8 -8
  20. package/dist/internal/createAppV3.d.ts.map +1 -1
  21. package/dist/internal/createAppV3.js +74 -83
  22. package/dist/internal/createAppV3.js.map +1 -1
  23. package/dist/internal/getServices.d.ts +5 -0
  24. package/dist/internal/getServices.d.ts.map +1 -0
  25. package/dist/internal/getServices.js +19 -0
  26. package/dist/internal/getServices.js.map +1 -0
  27. package/dist/internal/service_factories.d.ts +2 -2
  28. package/dist/internal/service_factories.d.ts.map +1 -1
  29. package/dist/internal/service_factories.js.map +1 -1
  30. package/dist/internal/utils.d.ts +3 -0
  31. package/dist/internal/utils.d.ts.map +1 -0
  32. package/dist/internal/utils.js +16 -0
  33. package/dist/internal/utils.js.map +1 -0
  34. package/package.json +7 -7
  35. package/src/components/PlAgColumnHeader/PlAgColumnHeader.vue +13 -7
  36. package/src/components/PlAgColumnHeader/types.ts +1 -0
  37. package/src/components/PlAgDataTable/sources/table-source-v2.ts +23 -21
  38. package/src/internal/createAppV3.ts +10 -37
  39. package/src/internal/getServices.ts +36 -0
  40. package/src/internal/service_factories.ts +2 -2
  41. package/src/internal/utils.ts +25 -0
@@ -1 +1 @@
1
- {"version":3,"file":"createAppV3.js","names":[],"sources":["../../src/internal/createAppV3.ts"],"sourcesContent":["import { deepClone, delay, uniqueId } from \"@milaboratories/helpers\";\nimport type { Mutable } from \"@milaboratories/helpers\";\nimport type {\n NavigationState,\n BlockOutputsBase,\n BlockStateV3,\n PlatformaV3,\n ValueWithUTag,\n AuthorMarker,\n PlatformaExtended,\n InferPluginHandles,\n PluginHandle,\n InferFactoryData,\n InferFactoryOutputs,\n InferFactoryUiServices,\n PluginFactoryLike,\n} from \"@platforma-sdk/model\";\nimport {\n hasAbortError,\n unwrapResult,\n deriveDataFromStorage,\n getPluginData,\n isPluginOutputKey,\n pluginOutputPrefix,\n createNodeServiceProxy,\n buildServices,\n} from \"@platforma-sdk/model\";\nimport { type UiServices as AllUiServices } from \"@milaboratories/pl-model-common\";\nimport { createUiServiceRegistry } from \"./service_factories\";\nimport type { Ref } from \"vue\";\nimport { reactive, computed, ref, markRaw } from \"vue\";\nimport type { OutputValues, OutputErrors, AppSettings } from \"../types\";\nimport { parseQuery } from \"../urls\";\nimport { ensureOutputHasStableFlag, MultiError } from \"../utils\";\nimport { applyPatch } from \"fast-json-patch\";\nimport { UpdateSerializer } from \"./UpdateSerializer\";\nimport { watchIgnorable } from \"@vueuse/core\";\nimport type { PluginState, PluginAccess } from \"../usePlugin\";\n\nexport const patchPoolingDelay = 150;\n\n/** Internal per-plugin state with reconciliation support. */\ninterface InternalPluginState<\n Data = unknown,\n Outputs = unknown,\n Services = Record<string, unknown>,\n> extends PluginState<Data, Outputs, Services> {\n readonly ignoreUpdates: (fn: () => void) => void;\n}\n\nexport const createNextAuthorMarker = (marker: AuthorMarker | undefined): AuthorMarker => ({\n authorId: marker?.authorId ?? uniqueId(),\n localVersion: (marker?.localVersion ?? 0) + 1,\n});\n\nconst stringifyForDebug = (v: unknown) => {\n try {\n return JSON.stringify(v, null, 2);\n } catch (err) {\n return err instanceof Error ? err.message : String(err);\n }\n};\n\n/**\n * Creates an application instance with reactive state management, outputs, and methods for state updates and navigation.\n *\n * @template Args - The type of arguments used in the application.\n * @template Outputs - The type of block outputs extending `BlockOutputsBase`.\n * @template Data - The type of the block data.\n * @template Href - The type of navigation href, defaulting to a string starting with `/`.\n *\n * @param state - Initial state of the application, including args, outputs, UI state, and navigation state.\n * @param platforma - A platform interface for interacting with block states.\n * @param settings - Application settings, such as debug flags.\n *\n * @returns A reactive application object with methods, getters, and state.\n */\nexport function createAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,\n>(\n state: ValueWithUTag<BlockStateV3<Data, Outputs, Href>>,\n platforma: PlatformaExtended<PlatformaV3<Data, Args, Outputs, Href, Plugins, UiServices>>,\n settings: AppSettings,\n) {\n const debug = (msg: string, ...rest: unknown[]) => {\n if (settings.debug) {\n console.log(\n `%c>>> %c${msg}`,\n \"color: orange; font-weight: bold\",\n \"color: orange\",\n ...rest.map((r) => stringifyForDebug(r)),\n );\n }\n };\n\n const error = (msg: string, ...rest: unknown[]) => {\n console.error(\n `%c>>> %c${msg}`,\n \"color: red; font-weight: bold\",\n \"color: red\",\n ...rest.map((r) => stringifyForDebug(r)),\n );\n };\n\n const data = {\n isExternalSnapshot: false,\n author: {\n authorId: uniqueId(),\n localVersion: 0,\n },\n };\n\n const nextAuthorMarker = () => {\n data.author = createNextAuthorMarker(data.author);\n debug(\"nextAuthorMarker\", data.author);\n return data.author;\n };\n\n const closedRef = ref(false);\n\n const uTagRef = ref(state.uTag);\n\n const debounceSpan = settings.debounceSpan ?? 200;\n\n const setDataQueue = new UpdateSerializer({ debounceSpan });\n const pluginDataQueues = new Map<PluginHandle, UpdateSerializer>();\n const getPluginDataQueue = (handle: PluginHandle): UpdateSerializer => {\n let queue = pluginDataQueues.get(handle);\n if (!queue) {\n queue = new UpdateSerializer({ debounceSpan });\n pluginDataQueues.set(handle, queue);\n }\n return queue;\n };\n const setNavigationStateQueue = new UpdateSerializer({ debounceSpan });\n\n /** Lazily-created per-plugin reactive states. */\n const pluginStates = new Map<PluginHandle, InternalPluginState>();\n /**\n * Reactive snapshot of the application state, including args, outputs, UI state, and navigation state.\n */\n const snapshot = ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>(state.value) as Ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>;\n\n const updateData = async (value: Data) => {\n return platforma.mutateStorage({ operation: \"update-block-data\", value }, nextAuthorMarker());\n };\n\n const updatePluginData = async (handle: PluginHandle, value: unknown) => {\n return platforma.mutateStorage(\n { operation: \"update-plugin-data\", pluginId: handle, value },\n nextAuthorMarker(),\n );\n };\n\n const setNavigationState = async (state: NavigationState<Href>) => {\n return platforma.setNavigationState(state);\n };\n\n const outputs = computed<OutputValues<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, outputWithStatus]) =>\n platforma.blockModelInfo.outputs[k]?.withStatus\n ? [k, ensureOutputHasStableFlag(outputWithStatus)]\n : [\n k,\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined,\n ],\n );\n return Object.fromEntries(entries);\n });\n\n const outputErrors = computed<OutputErrors<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, vOrErr]) => [\n k,\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined,\n ]);\n return Object.fromEntries(entries);\n });\n\n const appModel = reactive({\n apiVersion: 3,\n error: \"\",\n model: {\n data: deepClone(deriveDataFromStorage<Data>(snapshot.value.blockStorage)) as Data,\n outputs,\n outputErrors,\n },\n }) as {\n error: string;\n model: {\n data: Data;\n outputs: OutputValues<Outputs>;\n outputErrors: OutputErrors<Outputs>;\n };\n };\n\n const { ignoreUpdates } = watchIgnorable(\n () => appModel.model,\n (_newData) => {\n const newData = deepClone(_newData);\n debug(\"setDataQueue appModel.model, data\", newData.data);\n setDataQueue.run(() => updateData(newData.data).then(unwrapResult));\n },\n { deep: true },\n );\n\n const updateAppModel = (newData: { data: Data }) => {\n debug(\"updateAppModel\", newData);\n appModel.model.data = deepClone(newData.data) as Data;\n };\n\n (async () => {\n window.addEventListener(\"beforeunload\", () => {\n closedRef.value = true;\n Promise.allSettled([uiRegistry.dispose(), platforma.dispose().then(unwrapResult)]).catch(\n (err) => {\n error(\"error in dispose\", err);\n },\n );\n });\n\n while (!closedRef.value) {\n try {\n const patches = await platforma.getPatches(uTagRef.value).then(unwrapResult);\n\n debug(\"patches.length\", patches.value.length);\n debug(\"uTagRef.value\", uTagRef.value);\n debug(\"patches.uTag\", patches.uTag);\n debug(\"patches.author\", patches.author);\n debug(\"data.author\", data.author);\n\n uTagRef.value = patches.uTag;\n\n if (patches.value.length === 0) {\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n continue;\n }\n\n const isAuthorChanged = data.author?.authorId !== patches.author?.authorId;\n\n // Immutable behavior, apply external changes to the snapshot\n if (isAuthorChanged || data.isExternalSnapshot) {\n debug(\"got external changes, applying them to the snapshot\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value, false, false).newDocument;\n updateAppModel({ data: deriveDataFromStorage<Data>(snapshot.value.blockStorage) });\n // Reconcile plugin data from external source\n for (const [handle, pluginState] of pluginStates) {\n pluginState.ignoreUpdates(() => {\n pluginState.model.data = deepClone(\n getPluginData(snapshot.value.blockStorage, handle),\n );\n });\n }\n data.isExternalSnapshot = isAuthorChanged;\n });\n } else {\n // Mutable behavior\n debug(\"outputs changed\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value).newDocument;\n });\n }\n\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n } catch (err) {\n if (hasAbortError(err)) {\n debug(\"patches loop aborted\");\n closedRef.value = true;\n } else {\n error(\"error in patches loop\", err);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n }\n })();\n\n const cloneData = () => deepClone(appModel.model.data) as Data;\n const cloneNavigationState = () =>\n deepClone(snapshot.value.navigationState) as Mutable<NavigationState<Href>>;\n\n const methods = {\n cloneData,\n cloneNavigationState,\n /**\n * Updates the UI state by applying a callback.\n *\n * @param cb - Callback to modify the current UI state.\n * @returns A promise resolving after the update is applied.\n * @todo Make it mutable since there is already an initial one\n */\n updateData(cb: (data: Data) => Data): Promise<boolean> {\n const newData = cb(cloneData());\n debug(\"updateData\", newData);\n appModel.model.data = newData;\n return setDataQueue.run(() => updateData(newData).then(unwrapResult));\n },\n /**\n * Navigates to a specific href by updating the navigation state.\n *\n * @param href - The target href to navigate to.\n * @returns A promise resolving after the navigation state is updated.\n */\n navigateTo(href: Href) {\n const newState = cloneNavigationState();\n newState.href = href;\n return setNavigationStateQueue.run(() => setNavigationState(newState).then(unwrapResult));\n },\n async allSettled() {\n await delay(0);\n const allQueues = [\n setDataQueue.allSettled(),\n ...Array.from(pluginDataQueues.values()).map((q) => q.allSettled()),\n ];\n await Promise.all(allQueues);\n },\n };\n\n const proxy = createNodeServiceProxy(platforma.serviceDispatch);\n const uiRegistry = createUiServiceRegistry({ proxy });\n const services = buildServices<UiServices>(platforma.serviceDispatch, uiRegistry);\n\n /** Creates a lazily-cached per-plugin reactive state. */\n const createPluginState = <F extends PluginFactoryLike>(\n handle: PluginHandle<F>,\n ): InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>> => {\n const prefix = pluginOutputPrefix(handle);\n\n const pluginOutputs = computed(() => {\n const result: Record<string, unknown> = {};\n for (const [key, outputWithStatus] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n const outputKey = key.slice(prefix.length);\n if (platforma.blockModelInfo.outputs[key]?.withStatus) {\n result[outputKey] = outputWithStatus\n ? ensureOutputHasStableFlag(outputWithStatus)\n : undefined;\n } else {\n result[outputKey] =\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined;\n }\n }\n return result;\n });\n\n const pluginOutputErrors = computed(() => {\n const result: Record<string, Error | undefined> = {};\n for (const [key, vOrErr] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n result[key.slice(prefix.length)] =\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined;\n }\n return result;\n });\n\n const pluginModel = reactive({\n data: deepClone(getPluginData(snapshot.value.blockStorage, handle)),\n outputs: pluginOutputs,\n outputErrors: pluginOutputErrors,\n }) as InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>>[\"model\"];\n\n const { ignoreUpdates } = watchIgnorable(\n () => pluginModel.data,\n (newData) => {\n if (newData === undefined) return;\n debug(\"plugin setData\", handle, newData);\n getPluginDataQueue(handle).run(() =>\n updatePluginData(handle, deepClone(newData)).then(unwrapResult),\n );\n },\n { deep: true },\n );\n\n return {\n model: pluginModel,\n services: markRaw(services),\n ignoreUpdates,\n };\n };\n\n /** Plugin internals — provided via separate injection key, not exposed on useApp(). */\n const pluginAccess: PluginAccess = {\n getOrCreatePluginState<F extends PluginFactoryLike>(handle: PluginHandle<F>) {\n const existing = pluginStates.get(handle);\n if (existing) {\n return existing as unknown as PluginState<\n InferFactoryData<F>,\n InferFactoryOutputs<F>,\n InferFactoryUiServices<F>\n >;\n }\n const state = createPluginState(handle);\n pluginStates.set(handle, state);\n return state as unknown as PluginState<\n InferFactoryData<F>,\n InferFactoryOutputs<F>,\n InferFactoryUiServices<F>\n >;\n },\n };\n\n const plugins = Object.fromEntries(\n platforma.blockModelInfo.pluginIds.map((id) => [id, { handle: id }]),\n ) as InferPluginHandles<Plugins>;\n\n const getters = {\n closedRef,\n snapshot,\n plugins,\n services: markRaw(services),\n queryParams: computed(() => parseQuery<Href>(snapshot.value.navigationState.href as Href)),\n href: computed(() => snapshot.value.navigationState.href),\n hasErrors: computed(() =>\n Object.values(snapshot.value.outputs as Partial<Readonly<Outputs>>).some((v) => !v?.ok),\n ),\n };\n\n const app = Object.assign(reactive(Object.assign(appModel, getters)), methods);\n\n if (settings.debug) {\n // @ts-expect-error (to inspect in console in debug mode)\n globalThis.__block_app__ = app;\n }\n\n return { app, pluginAccess };\n}\n\nexport type BaseAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,\n> = ReturnType<typeof createAppV3<Data, Args, Outputs, Href, Plugins, UiServices>>[\"app\"];\n"],"mappings":"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"}
1
+ {"version":3,"file":"createAppV3.js","names":[],"sources":["../../src/internal/createAppV3.ts"],"sourcesContent":["import { deepClone, delay, uniqueId } from \"@milaboratories/helpers\";\nimport type { Mutable } from \"@milaboratories/helpers\";\nimport type {\n NavigationState,\n BlockOutputsBase,\n BlockStateV3,\n PlatformaV3,\n ValueWithUTag,\n AuthorMarker,\n PlatformaExtended,\n InferPluginHandles,\n PluginHandle,\n InferFactoryData,\n InferFactoryOutputs,\n InferFactoryUiServices,\n PluginFactoryLike,\n} from \"@platforma-sdk/model\";\nimport {\n hasAbortError,\n unwrapResult,\n deriveDataFromStorage,\n getPluginData,\n isPluginOutputKey,\n pluginOutputPrefix,\n} from \"@platforma-sdk/model\";\nimport { type UiServices as AllUiServices } from \"@milaboratories/pl-model-common\";\nimport type { Ref } from \"vue\";\nimport { reactive, computed, ref, markRaw } from \"vue\";\nimport type { OutputValues, OutputErrors, AppSettings } from \"../types\";\nimport { parseQuery } from \"../urls\";\nimport { ensureOutputHasStableFlag, MultiError } from \"../utils\";\nimport { applyPatch } from \"fast-json-patch\";\nimport { UpdateSerializer } from \"./UpdateSerializer\";\nimport { watchIgnorable } from \"@vueuse/core\";\nimport type { PluginState, PluginAccess } from \"../usePlugin\";\nimport { logDebug, logError } from \"./utils\";\nimport { getServices } from \"./getServices\";\n\nexport const patchPoolingDelay = 150;\n\n/** Internal per-plugin state with reconciliation support. */\ninterface InternalPluginState<\n Data = unknown,\n Outputs = unknown,\n Services = Record<string, unknown>,\n> extends PluginState<Data, Outputs, Services> {\n readonly ignoreUpdates: (fn: () => void) => void;\n}\n\nexport const createNextAuthorMarker = (marker: AuthorMarker | undefined): AuthorMarker => ({\n authorId: marker?.authorId ?? uniqueId(),\n localVersion: (marker?.localVersion ?? 0) + 1,\n});\n\n/**\n * Creates an application instance with reactive state management, outputs, and methods for state updates and navigation.\n *\n * @template Args - The type of arguments used in the application.\n * @template Outputs - The type of block outputs extending `BlockOutputsBase`.\n * @template Data - The type of the block data.\n * @template Href - The type of navigation href, defaulting to a string starting with `/`.\n *\n * @param state - Initial state of the application, including args, outputs, UI state, and navigation state.\n * @param platforma - A platform interface for interacting with block states.\n * @param settings - Application settings, such as debug flags.\n *\n * @returns A reactive application object with methods, getters, and state.\n */\nexport function createAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,\n>(\n state: ValueWithUTag<BlockStateV3<Data, Outputs, Href>>,\n platforma: PlatformaExtended<PlatformaV3<Data, Args, Outputs, Href, Plugins, UiServices>>,\n settings: AppSettings,\n) {\n const debug = settings.debug ? logDebug : () => {};\n const error = logError;\n\n const data = {\n isExternalSnapshot: false,\n author: {\n authorId: uniqueId(),\n localVersion: 0,\n },\n };\n\n const nextAuthorMarker = () => {\n data.author = createNextAuthorMarker(data.author);\n debug(\"nextAuthorMarker\", data.author);\n return data.author;\n };\n\n const closedRef = ref(false);\n\n const uTagRef = ref(state.uTag);\n\n const debounceSpan = settings.debounceSpan ?? 200;\n\n const setDataQueue = new UpdateSerializer({ debounceSpan });\n const pluginDataQueues = new Map<PluginHandle, UpdateSerializer>();\n const getPluginDataQueue = (handle: PluginHandle): UpdateSerializer => {\n let queue = pluginDataQueues.get(handle);\n if (!queue) {\n queue = new UpdateSerializer({ debounceSpan });\n pluginDataQueues.set(handle, queue);\n }\n return queue;\n };\n const setNavigationStateQueue = new UpdateSerializer({ debounceSpan });\n\n /** Lazily-created per-plugin reactive states. */\n const pluginStates = new Map<PluginHandle, InternalPluginState>();\n /**\n * Reactive snapshot of the application state, including args, outputs, UI state, and navigation state.\n */\n const snapshot = ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>(state.value) as Ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>;\n\n const updateData = async (value: Data) => {\n return platforma.mutateStorage({ operation: \"update-block-data\", value }, nextAuthorMarker());\n };\n\n const updatePluginData = async (handle: PluginHandle, value: unknown) => {\n return platforma.mutateStorage(\n { operation: \"update-plugin-data\", pluginId: handle, value },\n nextAuthorMarker(),\n );\n };\n\n const setNavigationState = async (state: NavigationState<Href>) => {\n return platforma.setNavigationState(state);\n };\n\n const outputs = computed<OutputValues<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, outputWithStatus]) =>\n platforma.blockModelInfo.outputs[k]?.withStatus\n ? [k, ensureOutputHasStableFlag(outputWithStatus)]\n : [\n k,\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined,\n ],\n );\n return Object.fromEntries(entries);\n });\n\n const outputErrors = computed<OutputErrors<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, vOrErr]) => [\n k,\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined,\n ]);\n return Object.fromEntries(entries);\n });\n\n const appModel = reactive({\n apiVersion: 3,\n error: \"\",\n model: {\n data: deepClone(deriveDataFromStorage<Data>(snapshot.value.blockStorage)) as Data,\n outputs,\n outputErrors,\n },\n }) as {\n error: string;\n model: {\n data: Data;\n outputs: OutputValues<Outputs>;\n outputErrors: OutputErrors<Outputs>;\n };\n };\n\n const { ignoreUpdates } = watchIgnorable(\n () => appModel.model,\n (_newData) => {\n const newData = deepClone(_newData);\n debug(\"setDataQueue appModel.model, data\", newData.data);\n setDataQueue.run(() => updateData(newData.data).then(unwrapResult));\n },\n { deep: true },\n );\n\n const updateAppModel = (newData: { data: Data }) => {\n debug(\"updateAppModel\", newData);\n appModel.model.data = deepClone(newData.data) as Data;\n };\n\n (async () => {\n window.addEventListener(\"beforeunload\", () => {\n closedRef.value = true;\n platforma\n .dispose()\n .then(unwrapResult)\n .catch((err) => {\n error(\"error in dispose\", err);\n });\n });\n\n while (!closedRef.value) {\n try {\n const patches = await platforma.getPatches(uTagRef.value).then(unwrapResult);\n\n debug(\"patches.length\", patches.value.length);\n debug(\"uTagRef.value\", uTagRef.value);\n debug(\"patches.uTag\", patches.uTag);\n debug(\"patches.author\", patches.author);\n debug(\"data.author\", data.author);\n\n uTagRef.value = patches.uTag;\n\n if (patches.value.length === 0) {\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n continue;\n }\n\n const isAuthorChanged = data.author?.authorId !== patches.author?.authorId;\n\n // Immutable behavior, apply external changes to the snapshot\n if (isAuthorChanged || data.isExternalSnapshot) {\n debug(\"got external changes, applying them to the snapshot\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value, false, false).newDocument;\n updateAppModel({ data: deriveDataFromStorage<Data>(snapshot.value.blockStorage) });\n // Reconcile plugin data from external source\n for (const [handle, pluginState] of pluginStates) {\n pluginState.ignoreUpdates(() => {\n pluginState.model.data = deepClone(\n getPluginData(snapshot.value.blockStorage, handle),\n );\n });\n }\n data.isExternalSnapshot = isAuthorChanged;\n });\n } else {\n // Mutable behavior\n debug(\"outputs changed\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value).newDocument;\n });\n }\n\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n } catch (err) {\n if (hasAbortError(err)) {\n debug(\"patches loop aborted\");\n closedRef.value = true;\n } else {\n error(\"error in patches loop\", err);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n }\n })();\n\n const cloneData = () => deepClone(appModel.model.data) as Data;\n const cloneNavigationState = () =>\n deepClone(snapshot.value.navigationState) as Mutable<NavigationState<Href>>;\n\n const methods = {\n cloneData,\n cloneNavigationState,\n /**\n * Updates the UI state by applying a callback.\n *\n * @param cb - Callback to modify the current UI state.\n * @returns A promise resolving after the update is applied.\n * @todo Make it mutable since there is already an initial one\n */\n updateData(cb: (data: Data) => Data): Promise<boolean> {\n const newData = cb(cloneData());\n debug(\"updateData\", newData);\n appModel.model.data = newData;\n return setDataQueue.run(() => updateData(newData).then(unwrapResult));\n },\n /**\n * Navigates to a specific href by updating the navigation state.\n *\n * @param href - The target href to navigate to.\n * @returns A promise resolving after the navigation state is updated.\n */\n navigateTo(href: Href) {\n const newState = cloneNavigationState();\n newState.href = href;\n return setNavigationStateQueue.run(() => setNavigationState(newState).then(unwrapResult));\n },\n async allSettled() {\n await delay(0);\n const allQueues = [\n setDataQueue.allSettled(),\n ...Array.from(pluginDataQueues.values()).map((q) => q.allSettled()),\n ];\n await Promise.all(allQueues);\n },\n };\n\n const services = getServices<UiServices>({ platforma });\n\n /** Creates a lazily-cached per-plugin reactive state. */\n const createPluginState = <F extends PluginFactoryLike>(\n handle: PluginHandle<F>,\n ): InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>> => {\n const prefix = pluginOutputPrefix(handle);\n\n const pluginOutputs = computed(() => {\n const result: Record<string, unknown> = {};\n for (const [key, outputWithStatus] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n const outputKey = key.slice(prefix.length);\n if (platforma.blockModelInfo.outputs[key]?.withStatus) {\n result[outputKey] = outputWithStatus\n ? ensureOutputHasStableFlag(outputWithStatus)\n : undefined;\n } else {\n result[outputKey] =\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined;\n }\n }\n return result;\n });\n\n const pluginOutputErrors = computed(() => {\n const result: Record<string, Error | undefined> = {};\n for (const [key, vOrErr] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n result[key.slice(prefix.length)] =\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined;\n }\n return result;\n });\n\n const pluginModel = reactive({\n data: deepClone(getPluginData(snapshot.value.blockStorage, handle)),\n outputs: pluginOutputs,\n outputErrors: pluginOutputErrors,\n }) as InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>>[\"model\"];\n\n const { ignoreUpdates } = watchIgnorable(\n () => pluginModel.data,\n (newData) => {\n if (newData === undefined) return;\n debug(\"plugin setData\", handle, newData);\n getPluginDataQueue(handle).run(() =>\n updatePluginData(handle, deepClone(newData)).then(unwrapResult),\n );\n },\n { deep: true },\n );\n\n return {\n model: pluginModel,\n services: markRaw(services),\n ignoreUpdates,\n };\n };\n\n /** Plugin internals — provided via separate injection key, not exposed on useApp(). */\n const pluginAccess: PluginAccess = {\n getOrCreatePluginState<F extends PluginFactoryLike>(handle: PluginHandle<F>) {\n const existing = pluginStates.get(handle);\n if (existing) {\n return existing as unknown as PluginState<\n InferFactoryData<F>,\n InferFactoryOutputs<F>,\n InferFactoryUiServices<F>\n >;\n }\n const state = createPluginState(handle);\n pluginStates.set(handle, state);\n return state as unknown as PluginState<\n InferFactoryData<F>,\n InferFactoryOutputs<F>,\n InferFactoryUiServices<F>\n >;\n },\n };\n\n const plugins = Object.fromEntries(\n platforma.blockModelInfo.pluginIds.map((id) => [id, { handle: id }]),\n ) as InferPluginHandles<Plugins>;\n\n const getters = {\n closedRef,\n snapshot,\n plugins,\n services: markRaw(services),\n queryParams: computed(() => parseQuery<Href>(snapshot.value.navigationState.href as Href)),\n href: computed(() => snapshot.value.navigationState.href),\n hasErrors: computed(() =>\n Object.values(snapshot.value.outputs as Partial<Readonly<Outputs>>).some((v) => !v?.ok),\n ),\n };\n\n const app = Object.assign(reactive(Object.assign(appModel, getters)), methods);\n\n if (settings.debug) {\n // @ts-expect-error (to inspect in console in debug mode)\n globalThis.__block_app__ = app;\n }\n\n return { app, pluginAccess };\n}\n\nexport type BaseAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,\n> = ReturnType<typeof createAppV3<Data, Args, Outputs, Href, Plugins, UiServices>>[\"app\"];\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { BlockDefaultUiServices, PlatformaV3, UiServices } from '@platforma-sdk/model';
2
+ export declare function getServices<Services extends Partial<UiServices> = BlockDefaultUiServices>(deps?: {
3
+ platforma?: PlatformaV3<any, any, any, any, any, Services>;
4
+ }): Services;
5
+ //# sourceMappingURL=getServices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getServices.d.ts","sourceRoot":"","sources":["../../src/internal/getServices.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EAItB,WAAW,EACX,UAAU,EACX,MAAM,sBAAsB,CAAC;AAO9B,wBAAgB,WAAW,CAAC,QAAQ,SAAS,OAAO,CAAC,UAAU,CAAC,GAAG,sBAAsB,EAAE,IAAI,CAAC,EAAE;IAChG,SAAS,CAAC,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;CAC5D,GAAG,QAAQ,CAmBX"}
@@ -0,0 +1,19 @@
1
+ import { logError as e } from "./utils.js";
2
+ import { createUiServiceRegistry as t } from "./service_factories.js";
3
+ import { buildServices as n, createServiceProxy as r, getRawPlatformaInstance as i } from "@platforma-sdk/model";
4
+ import { isNil as a } from "es-toolkit";
5
+ //#region src/internal/getServices.ts
6
+ var o = null;
7
+ function s(s) {
8
+ if (!a(o)) return o;
9
+ let c = s?.platforma ?? i(), l = t({ proxy: r(c.serviceDispatch) }), u = n(c.serviceDispatch, l);
10
+ return window.addEventListener("beforeunload", () => {
11
+ l.dispose().catch((t) => {
12
+ e("uiRegistry error in dispose", t);
13
+ });
14
+ }), o = u;
15
+ }
16
+ //#endregion
17
+ export { s as getServices };
18
+
19
+ //# sourceMappingURL=getServices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getServices.js","names":[],"sources":["../../src/internal/getServices.ts"],"sourcesContent":["import {\n BlockDefaultUiServices,\n buildServices,\n createServiceProxy,\n getRawPlatformaInstance,\n PlatformaV3,\n UiServices,\n} from \"@platforma-sdk/model\";\nimport { createUiServiceRegistry } from \"./service_factories\";\nimport { isNil } from \"es-toolkit\";\nimport { logError } from \"./utils\";\n\nlet cachedServices: null | Partial<UiServices> = null;\n\nexport function getServices<Services extends Partial<UiServices> = BlockDefaultUiServices>(deps?: {\n platforma?: PlatformaV3<any, any, any, any, any, Services>;\n}): Services {\n if (!isNil(cachedServices)) {\n return cachedServices as Services;\n }\n\n const platforma =\n deps?.platforma ??\n (getRawPlatformaInstance() as PlatformaV3<any, any, any, any, any, Services>);\n const proxy = createServiceProxy(platforma.serviceDispatch);\n const uiRegistry = createUiServiceRegistry({ proxy });\n const services = buildServices<Services>(platforma.serviceDispatch, uiRegistry);\n\n window.addEventListener(\"beforeunload\", () => {\n uiRegistry.dispose().catch((err) => {\n logError(\"uiRegistry error in dispose\", err);\n });\n });\n\n return (cachedServices = services) as Services;\n}\n"],"mappings":";;;;;AAYA,IAAI,IAA6C;AAEjD,SAAgB,EAA2E,GAE9E;AACX,KAAI,CAAC,EAAM,EAAe,CACxB,QAAO;CAGT,IAAM,IACJ,GAAM,aACL,GAAyB,EAEtB,IAAa,EAAwB,EAAE,OAD/B,EAAmB,EAAU,gBAAgB,EACP,CAAC,EAC/C,IAAW,EAAwB,EAAU,iBAAiB,EAAW;AAQ/E,QANA,OAAO,iBAAiB,sBAAsB;AAC5C,IAAW,SAAS,CAAC,OAAO,MAAQ;AAClC,KAAS,+BAA+B,EAAI;IAC5C;GACF,EAEM,IAAiB"}
@@ -1,7 +1,7 @@
1
1
  import { UiServiceRegistry } from '@milaboratories/pl-model-common';
2
- import { NodeServiceProxy } from '@platforma-sdk/model';
2
+ import { ServiceProxy } from '@platforma-sdk/model';
3
3
  export type UiServiceOptions = {
4
- proxy: NodeServiceProxy;
4
+ proxy: ServiceProxy;
5
5
  };
6
6
  export declare function createUiServiceRegistry(options: UiServiceOptions): UiServiceRegistry<{
7
7
  PFrameSpec: import('@milaboratories/pl-model-common').Branded<"pframeSpec", import('@milaboratories/pl-model-common').ServiceTypesLike<import('@milaboratories/pl-model-common').PFrameSpecDriver, import('@milaboratories/pl-model-common').PFrameSpecDriver, "wasm">>;
@@ -1 +1 @@
1
- {"version":3,"file":"service_factories.d.ts","sourceRoot":"","sources":["../../src/internal/service_factories.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAE9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;;;GAKhE"}
1
+ {"version":3,"file":"service_factories.d.ts","sourceRoot":"","sources":["../../src/internal/service_factories.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAE9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;;;GAKhE"}
@@ -1 +1 @@
1
- {"version":3,"file":"service_factories.js","names":[],"sources":["../../src/internal/service_factories.ts"],"sourcesContent":["/**\n * UI service factories — add a factory for each new service here.\n *\n * Each entry maps a Services key to a factory function that creates the\n * UI-side driver instance:\n * - WASM services: instantiated directly (e.g. SpecDriver)\n * - Node services: proxied via IPC using NodeServiceProxy\n */\n\nimport { Services, UiServiceRegistry } from \"@milaboratories/pl-model-common\";\nimport { SpecDriver } from \"@milaboratories/pf-spec-driver\";\nimport type { NodeServiceProxy } from \"@platforma-sdk/model\";\n\nexport type UiServiceOptions = {\n proxy: NodeServiceProxy;\n};\n\nexport function createUiServiceRegistry(options: UiServiceOptions) {\n return new UiServiceRegistry(Services, {\n PFrameSpec: () => new SpecDriver(),\n PFrame: () => options.proxy(Services.PFrame),\n });\n}\n"],"mappings":";;;AAiBA,SAAgB,EAAwB,GAA2B;AACjE,QAAO,IAAI,EAAkB,GAAU;EACrC,kBAAkB,IAAI,GAAY;EAClC,cAAc,EAAQ,MAAM,EAAS,OAAO;EAC7C,CAAC"}
1
+ {"version":3,"file":"service_factories.js","names":[],"sources":["../../src/internal/service_factories.ts"],"sourcesContent":["/**\n * UI service factories — add a factory for each new service here.\n *\n * Each entry maps a Services key to a factory function that creates the\n * UI-side driver instance:\n * - WASM services: instantiated directly (e.g. SpecDriver)\n * - Node services: proxied via IPC using NodeServiceProxy\n */\n\nimport { Services, UiServiceRegistry } from \"@milaboratories/pl-model-common\";\nimport { SpecDriver } from \"@milaboratories/pf-spec-driver\";\nimport type { ServiceProxy } from \"@platforma-sdk/model\";\n\nexport type UiServiceOptions = {\n proxy: ServiceProxy;\n};\n\nexport function createUiServiceRegistry(options: UiServiceOptions) {\n return new UiServiceRegistry(Services, {\n PFrameSpec: () => new SpecDriver(),\n PFrame: () => options.proxy(Services.PFrame),\n });\n}\n"],"mappings":";;;AAiBA,SAAgB,EAAwB,GAA2B;AACjE,QAAO,IAAI,EAAkB,GAAU;EACrC,kBAAkB,IAAI,GAAY;EAClC,cAAc,EAAQ,MAAM,EAAS,OAAO;EAC7C,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const logDebug: (msg: string, ...rest: unknown[]) => void;
2
+ export declare const logError: (msg: string, ...rest: unknown[]) => void;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,GAAG,MAAM,OAAO,EAAE,SAOvD,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,GAAG,MAAM,OAAO,EAAE,SAOvD,CAAC"}
@@ -0,0 +1,16 @@
1
+ //#region src/internal/utils.ts
2
+ var e = (e, ...t) => {
3
+ console.log(`%c>>> %c${e}`, "color: orange; font-weight: bold", "color: orange", ...t.map((e) => n(e)));
4
+ }, t = (e, ...t) => {
5
+ console.error(`%c>>> %c${e}`, "color: red; font-weight: bold", "color: red", ...t.map((e) => n(e)));
6
+ }, n = (e) => {
7
+ try {
8
+ return JSON.stringify(e, null, 2);
9
+ } catch (e) {
10
+ return e instanceof Error ? e.message : String(e);
11
+ }
12
+ };
13
+ //#endregion
14
+ export { e as logDebug, t as logError };
15
+
16
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/internal/utils.ts"],"sourcesContent":["export const logDebug = (msg: string, ...rest: unknown[]) => {\n console.log(\n `%c>>> %c${msg}`,\n \"color: orange; font-weight: bold\",\n \"color: orange\",\n ...rest.map((r) => stringifyForDebug(r)),\n );\n};\n\nexport const logError = (msg: string, ...rest: unknown[]) => {\n console.error(\n `%c>>> %c${msg}`,\n \"color: red; font-weight: bold\",\n \"color: red\",\n ...rest.map((r) => stringifyForDebug(r)),\n );\n};\n\nconst stringifyForDebug = (v: unknown) => {\n try {\n return JSON.stringify(v, null, 2);\n } catch (err) {\n return err instanceof Error ? err.message : String(err);\n }\n};\n"],"mappings":";AAAA,IAAa,KAAY,GAAa,GAAG,MAAoB;AAC3D,SAAQ,IACN,WAAW,KACX,oCACA,iBACA,GAAG,EAAK,KAAK,MAAM,EAAkB,EAAE,CAAC,CACzC;GAGU,KAAY,GAAa,GAAG,MAAoB;AAC3D,SAAQ,MACN,WAAW,KACX,iCACA,cACA,GAAG,EAAK,KAAK,MAAM,EAAkB,EAAE,CAAC,CACzC;GAGG,KAAqB,MAAe;AACxC,KAAI;AACF,SAAO,KAAK,UAAU,GAAG,MAAM,EAAE;UAC1B,GAAK;AACZ,SAAO,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.66.1",
3
+ "version": "1.66.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -26,10 +26,10 @@
26
26
  "lru-cache": "^11.2.2",
27
27
  "vue": "^3.5.24",
28
28
  "zod": "~3.25.76",
29
- "@milaboratories/pf-spec-driver": "1.3.1",
30
- "@milaboratories/uikit": "2.12.7",
31
- "@platforma-sdk/model": "1.65.10",
32
- "@milaboratories/pl-model-common": "1.34.0"
29
+ "@milaboratories/pl-model-common": "1.34.1",
30
+ "@milaboratories/pf-spec-driver": "1.3.2",
31
+ "@milaboratories/uikit": "2.12.8",
32
+ "@platforma-sdk/model": "1.66.2"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@faker-js/faker": "^9.2.0",
@@ -43,10 +43,10 @@
43
43
  "typescript": "~5.9.3",
44
44
  "vite": "^8.0.6",
45
45
  "vitest": "^4.1.3",
46
- "@milaboratories/build-configs": "2.0.0",
46
+ "@milaboratories/ts-builder": "1.3.2",
47
47
  "@milaboratories/helpers": "1.14.1",
48
48
  "@milaboratories/ts-configs": "1.2.3",
49
- "@milaboratories/ts-builder": "1.3.2"
49
+ "@milaboratories/build-configs": "2.0.0"
50
50
  },
51
51
  "scripts": {
52
52
  "dev": "ts-builder serve --target browser-lib",
@@ -64,14 +64,20 @@ function showMenu() {
64
64
 
65
65
  <template>
66
66
  <div class="pl-ag-column-header d-flex align-center gap-6" @click="onSortRequested">
67
- <PlTooltip>
68
- <template v-if="params.tooltip" #tooltip>{{ params.tooltip }}</template>
69
- <div class="pl-ag-column-header__title d-flex align-center gap-6 flex-grow-1">
70
- <PlMaskIcon16 :name="icon" class="pl-ag-column-header__type-icon" />
67
+ <div class="pl-ag-column-header__title d-flex align-center gap-6 flex-grow-1">
68
+ <PlMaskIcon16 :name="icon" class="pl-ag-column-header__type-icon" />
69
+ <PlTooltip>
70
+ <template v-if="params.tooltip" #tooltip>{{ params.tooltip }}</template>
71
71
  <span>{{ params.displayName }}</span>
72
- <PlMaskIcon16 v-if="sortIcon" :name="sortIcon" />
73
- </div>
74
- </PlTooltip>
72
+ </PlTooltip>
73
+ <PlTooltip v-if="params.info" max-width="500px" position="bottom" :close-delay="10000000000">
74
+ <template #tooltip>
75
+ <span style="white-space: pre-wrap">{{ params.info }}</span>
76
+ </template>
77
+ <PlMaskIcon16 name="info" />
78
+ </PlTooltip>
79
+ <PlMaskIcon16 v-if="sortIcon" :name="sortIcon" />
80
+ </div>
75
81
  <div
76
82
  v-if="params.enableMenu"
77
83
  ref="menuActivatorBtn"
@@ -3,4 +3,5 @@ export type PlAgHeaderComponentType = "Text" | "Number" | "File" | "Date" | "Dur
3
3
  export type PlAgHeaderComponentParams = {
4
4
  type?: PlAgHeaderComponentType;
5
5
  tooltip?: string;
6
+ info?: string;
6
7
  };
@@ -63,15 +63,15 @@ function columns2rows(
63
63
  axesResultIndices: number[],
64
64
  ): PlAgDataTableV2Row[] {
65
65
  const rowData: PlAgDataTableV2Row[] = [];
66
- for (let iRow = 0; iRow < columns[0].data.length; ++iRow) {
67
- const axesKey: PTableKey = axesResultIndices.map((ri) => pTableValue(columns[ri], iRow));
66
+ for (let rowIdx = 0; rowIdx < columns[0].data.length; ++rowIdx) {
67
+ const axesKey: PTableKey = axesResultIndices.map((ri) => pTableValue(columns[ri], rowIdx));
68
68
  const id = canonicalizeJson<PlTableRowId>(axesKey);
69
69
  const row = fields.reduce<PlAgDataTableV2Row>(
70
- (acc, field, iCol) => {
70
+ (acc, field, colIdx) => {
71
71
  acc[field.toString() as `${number}`] =
72
- fieldResultMapping[iCol] === -1
72
+ fieldResultMapping[colIdx] === -1
73
73
  ? PTableHidden
74
- : pTableValue(columns[fieldResultMapping[iCol]], iRow);
74
+ : pTableValue(columns[fieldResultMapping[colIdx]], rowIdx);
75
75
  return acc;
76
76
  },
77
77
  { id, axesKey },
@@ -156,10 +156,10 @@ export async function calculateGridOptions({
156
156
  // request indices: non-hidden display fields + visible axes for row selection keys.
157
157
  // Axes replaced by label columns request label data (display); original axis values
158
158
  // are fetched via visibleAxes (row keys).
159
- const { requestIndices, fieldResultMapping, axesResultIndices } = buildRequestIndices(
159
+ const { requestIndices, axesResultIndices, fieldResultMapping } = buildRequestIndices(
160
160
  indices,
161
+ visibleAxes.map(([idx]) => idx),
161
162
  specsToVisibleSpecsMapping,
162
- visibleAxes.map(([i]) => i),
163
163
  );
164
164
 
165
165
  let rowCount = -1;
@@ -313,7 +313,12 @@ export function makeColDef(
313
313
  throw Error(`unsupported data type: ${valueType}`);
314
314
  }
315
315
  })(),
316
- tooltip: readAnnotation(labeledSpec.spec, Annotation.Description)?.trim(),
316
+ tooltip:
317
+ readAnnotation(spec.spec, Annotation.Description)?.trim() ??
318
+ readAnnotation(labeledSpec.spec, Annotation.Description)?.trim(),
319
+ info:
320
+ readAnnotation(spec.spec, Annotation.Table.Info)?.trim() ??
321
+ readAnnotation(labeledSpec.spec, Annotation.Table.Info)?.trim(),
317
322
  } satisfies PlAgHeaderComponentParams,
318
323
  cellDataType: (() => {
319
324
  switch (valueType) {
@@ -465,26 +470,23 @@ function collectVisibleAxes(
465
470
  */
466
471
  function buildRequestIndices(
467
472
  indices: number[],
468
- specsToVisibleSpecsMapping: Map<number, number>,
469
473
  visibleAxesIndices: number[],
474
+ specsToVisibleSpecsMapping: Map<number, number>,
470
475
  ): {
471
476
  requestIndices: number[];
472
- fieldResultMapping: number[];
473
477
  axesResultIndices: number[];
478
+ fieldResultMapping: number[];
474
479
  } {
475
480
  const resolved = indices.map((displayField) => {
476
481
  const idx = specsToVisibleSpecsMapping.get(displayField);
477
- return idx === undefined || idx === -1 ? null : idx;
482
+ return isNil(idx) || idx === -1 ? null : idx;
478
483
  });
479
- const requestedFields = resolved.filter((v): v is number => v !== null);
480
-
481
- const fieldResultMapping: number[] = [];
482
- let pos = 0;
483
- for (const v of resolved) {
484
- fieldResultMapping.push(v === null ? -1 : pos++);
485
- }
486
-
487
- const requestIndices = uniq([...requestedFields, ...visibleAxesIndices]);
484
+ const requestIndices = uniq([
485
+ ...resolved.filter((v): v is number => v !== null),
486
+ ...visibleAxesIndices,
487
+ ]);
488
+ const fieldResultMapping = resolved.map((v) => (v === null ? -1 : requestIndices.indexOf(v)));
488
489
  const axesResultIndices = visibleAxesIndices.map((vi) => requestIndices.indexOf(vi));
489
- return { requestIndices, fieldResultMapping, axesResultIndices };
490
+
491
+ return { requestIndices, axesResultIndices, fieldResultMapping };
490
492
  }
@@ -22,11 +22,8 @@ import {
22
22
  getPluginData,
23
23
  isPluginOutputKey,
24
24
  pluginOutputPrefix,
25
- createNodeServiceProxy,
26
- buildServices,
27
25
  } from "@platforma-sdk/model";
28
26
  import { type UiServices as AllUiServices } from "@milaboratories/pl-model-common";
29
- import { createUiServiceRegistry } from "./service_factories";
30
27
  import type { Ref } from "vue";
31
28
  import { reactive, computed, ref, markRaw } from "vue";
32
29
  import type { OutputValues, OutputErrors, AppSettings } from "../types";
@@ -36,6 +33,8 @@ import { applyPatch } from "fast-json-patch";
36
33
  import { UpdateSerializer } from "./UpdateSerializer";
37
34
  import { watchIgnorable } from "@vueuse/core";
38
35
  import type { PluginState, PluginAccess } from "../usePlugin";
36
+ import { logDebug, logError } from "./utils";
37
+ import { getServices } from "./getServices";
39
38
 
40
39
  export const patchPoolingDelay = 150;
41
40
 
@@ -53,14 +52,6 @@ export const createNextAuthorMarker = (marker: AuthorMarker | undefined): Author
53
52
  localVersion: (marker?.localVersion ?? 0) + 1,
54
53
  });
55
54
 
56
- const stringifyForDebug = (v: unknown) => {
57
- try {
58
- return JSON.stringify(v, null, 2);
59
- } catch (err) {
60
- return err instanceof Error ? err.message : String(err);
61
- }
62
- };
63
-
64
55
  /**
65
56
  * Creates an application instance with reactive state management, outputs, and methods for state updates and navigation.
66
57
  *
@@ -87,25 +78,8 @@ export function createAppV3<
87
78
  platforma: PlatformaExtended<PlatformaV3<Data, Args, Outputs, Href, Plugins, UiServices>>,
88
79
  settings: AppSettings,
89
80
  ) {
90
- const debug = (msg: string, ...rest: unknown[]) => {
91
- if (settings.debug) {
92
- console.log(
93
- `%c>>> %c${msg}`,
94
- "color: orange; font-weight: bold",
95
- "color: orange",
96
- ...rest.map((r) => stringifyForDebug(r)),
97
- );
98
- }
99
- };
100
-
101
- const error = (msg: string, ...rest: unknown[]) => {
102
- console.error(
103
- `%c>>> %c${msg}`,
104
- "color: red; font-weight: bold",
105
- "color: red",
106
- ...rest.map((r) => stringifyForDebug(r)),
107
- );
108
- };
81
+ const debug = settings.debug ? logDebug : () => {};
82
+ const error = logError;
109
83
 
110
84
  const data = {
111
85
  isExternalSnapshot: false,
@@ -230,11 +204,12 @@ export function createAppV3<
230
204
  (async () => {
231
205
  window.addEventListener("beforeunload", () => {
232
206
  closedRef.value = true;
233
- Promise.allSettled([uiRegistry.dispose(), platforma.dispose().then(unwrapResult)]).catch(
234
- (err) => {
207
+ platforma
208
+ .dispose()
209
+ .then(unwrapResult)
210
+ .catch((err) => {
235
211
  error("error in dispose", err);
236
- },
237
- );
212
+ });
238
213
  });
239
214
 
240
215
  while (!closedRef.value) {
@@ -334,9 +309,7 @@ export function createAppV3<
334
309
  },
335
310
  };
336
311
 
337
- const proxy = createNodeServiceProxy(platforma.serviceDispatch);
338
- const uiRegistry = createUiServiceRegistry({ proxy });
339
- const services = buildServices<UiServices>(platforma.serviceDispatch, uiRegistry);
312
+ const services = getServices<UiServices>({ platforma });
340
313
 
341
314
  /** Creates a lazily-cached per-plugin reactive state. */
342
315
  const createPluginState = <F extends PluginFactoryLike>(
@@ -0,0 +1,36 @@
1
+ import {
2
+ BlockDefaultUiServices,
3
+ buildServices,
4
+ createServiceProxy,
5
+ getRawPlatformaInstance,
6
+ PlatformaV3,
7
+ UiServices,
8
+ } from "@platforma-sdk/model";
9
+ import { createUiServiceRegistry } from "./service_factories";
10
+ import { isNil } from "es-toolkit";
11
+ import { logError } from "./utils";
12
+
13
+ let cachedServices: null | Partial<UiServices> = null;
14
+
15
+ export function getServices<Services extends Partial<UiServices> = BlockDefaultUiServices>(deps?: {
16
+ platforma?: PlatformaV3<any, any, any, any, any, Services>;
17
+ }): Services {
18
+ if (!isNil(cachedServices)) {
19
+ return cachedServices as Services;
20
+ }
21
+
22
+ const platforma =
23
+ deps?.platforma ??
24
+ (getRawPlatformaInstance() as PlatformaV3<any, any, any, any, any, Services>);
25
+ const proxy = createServiceProxy(platforma.serviceDispatch);
26
+ const uiRegistry = createUiServiceRegistry({ proxy });
27
+ const services = buildServices<Services>(platforma.serviceDispatch, uiRegistry);
28
+
29
+ window.addEventListener("beforeunload", () => {
30
+ uiRegistry.dispose().catch((err) => {
31
+ logError("uiRegistry error in dispose", err);
32
+ });
33
+ });
34
+
35
+ return (cachedServices = services) as Services;
36
+ }
@@ -9,10 +9,10 @@
9
9
 
10
10
  import { Services, UiServiceRegistry } from "@milaboratories/pl-model-common";
11
11
  import { SpecDriver } from "@milaboratories/pf-spec-driver";
12
- import type { NodeServiceProxy } from "@platforma-sdk/model";
12
+ import type { ServiceProxy } from "@platforma-sdk/model";
13
13
 
14
14
  export type UiServiceOptions = {
15
- proxy: NodeServiceProxy;
15
+ proxy: ServiceProxy;
16
16
  };
17
17
 
18
18
  export function createUiServiceRegistry(options: UiServiceOptions) {
@@ -0,0 +1,25 @@
1
+ export const logDebug = (msg: string, ...rest: unknown[]) => {
2
+ console.log(
3
+ `%c>>> %c${msg}`,
4
+ "color: orange; font-weight: bold",
5
+ "color: orange",
6
+ ...rest.map((r) => stringifyForDebug(r)),
7
+ );
8
+ };
9
+
10
+ export const logError = (msg: string, ...rest: unknown[]) => {
11
+ console.error(
12
+ `%c>>> %c${msg}`,
13
+ "color: red; font-weight: bold",
14
+ "color: red",
15
+ ...rest.map((r) => stringifyForDebug(r)),
16
+ );
17
+ };
18
+
19
+ const stringifyForDebug = (v: unknown) => {
20
+ try {
21
+ return JSON.stringify(v, null, 2);
22
+ } catch (err) {
23
+ return err instanceof Error ? err.message : String(err);
24
+ }
25
+ };