From 04ff36f572a2fd42faa30a1221fc820238b9b8a7 Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 08:56:48 -0700 Subject: [PATCH 01/10] feat: extend BlockData type chain with dismissedInfoMessages Rename current BlockData -> BlockDataV2_1 (intermediate, output of Ver_2026_05_18). New current BlockData adds dismissedInfoMessages: string[] - persistent list of info-alert strings the user has closed. The Ver_2026_05_27 migration that backfills [] on existing projects is added in a follow-up commit (Task 2 of the plan). --- model/src/types.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/model/src/types.ts b/model/src/types.ts index 6fff0bd..a8d436b 100644 --- a/model/src/types.ts +++ b/model/src/types.ts @@ -23,11 +23,22 @@ export type BlockDataV2 = Omit & { graphStateHistogram: GraphMakerState; }; -export type BlockData = Omit & { +// V2.1 shape — what the deployed Ver_2026_05_18 migration produces. Input +// to the new Ver_2026_05_27 step that adds dismissedInfoMessages. Both +// label fields are required here (Ver_2026_05_18 backfills them). +export type BlockDataV2_1 = Omit & { defaultBlockLabel: string; customBlockLabel: string; }; +// Current shape — output of Ver_2026_05_27. Adds dismissedInfoMessages, +// the persistent list of info-alert strings the user has closed. +// Workflow `info.messages` strings are deterministic per input, so the +// string content itself is a stable dismissal key. +export type BlockData = BlockDataV2_1 & { + dismissedInfoMessages: string[]; +}; + export type BlockArgs = { inputAnchor: PlRef; traceLabel: string; From 87ade842c13e257b34040676516707c843d961ee Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 09:02:28 -0700 Subject: [PATCH 02/10] feat: add Ver_2026_05_27 migration backfilling dismissedInfoMessages New migration step appends to the data-model chain; backfills the persisted dismissal list as [] on existing V2.1 projects. Unit tests mirror the Ver_2026_05_18 pattern: bare-backfill, interim-value preservation, full chain from V1. Also restores type-check health in label.test.ts by adding the new field to its base fixture. --- model/src/dataModel.test.ts | 43 +++++++++++++++++++++++++++++++------ model/src/dataModel.ts | 19 +++++++++++++--- model/src/label.test.ts | 1 + 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/model/src/dataModel.test.ts b/model/src/dataModel.test.ts index 5e80700..466c0aa 100644 --- a/model/src/dataModel.test.ts +++ b/model/src/dataModel.test.ts @@ -5,8 +5,8 @@ // deleted a `.migrate(...)` line while keeping the named callback exported. import { describe, expect, it } from "vitest"; -import type { BlockData, BlockDataV1, BlockDataV2 } from "./types"; -import { migrateV1toV2, migrateV2toV2_1 } from "./dataModel"; +import type { BlockData, BlockDataV1, BlockDataV2, BlockDataV2_1 } from "./types"; +import { migrateV1toV2, migrateV2toV2_1, migrateV2_1toV2_2 } from "./dataModel"; const tableState = { pTableParams: { @@ -33,31 +33,62 @@ const v2Graph: Pick = describe("blockDataModel Ver_2026_05_18 backfill", () => { it("backfills both label fields to '' on a bare V2 payload", () => { const v2: BlockDataV2 = { tableState, ...v2Graph }; - const upgraded: BlockData = migrateV2toV2_1(v2); + const upgraded: BlockDataV2_1 = migrateV2toV2_1(v2); expect(upgraded.customBlockLabel).toBe(""); expect(upgraded.defaultBlockLabel).toBe(""); }); it("preserves an interim-deployed customBlockLabel", () => { const v2: BlockDataV2 = { tableState, ...v2Graph, customBlockLabel: "X" }; - const upgraded: BlockData = migrateV2toV2_1(v2); + const upgraded: BlockDataV2_1 = migrateV2toV2_1(v2); expect(upgraded.customBlockLabel).toBe("X"); expect(upgraded.defaultBlockLabel).toBe(""); }); it("preserves an interim-deployed defaultBlockLabel", () => { const v2: BlockDataV2 = { tableState, ...v2Graph, defaultBlockLabel: "Y" }; - const upgraded: BlockData = migrateV2toV2_1(v2); + const upgraded: BlockDataV2_1 = migrateV2toV2_1(v2); expect(upgraded.defaultBlockLabel).toBe("Y"); expect(upgraded.customBlockLabel).toBe(""); }); it("runs the full V1 → V2 → V2.1 chain on legacy data", () => { const v1: BlockDataV1 = { tableState, defaultBlockLabel: "Old" }; - const upgraded: BlockData = migrateV2toV2_1(migrateV1toV2(v1)); + const upgraded: BlockDataV2_1 = migrateV2toV2_1(migrateV1toV2(v1)); expect(upgraded.defaultBlockLabel).toBe("Old"); expect(upgraded.customBlockLabel).toBe(""); expect(upgraded.graphStateScatter).toBeDefined(); expect(upgraded.graphStateHistogram).toBeDefined(); }); }); + +describe("blockDataModel Ver_2026_05_27 backfill", () => { + const baseV2_1: BlockDataV2_1 = { + tableState, + ...v2Graph, + defaultBlockLabel: "", + customBlockLabel: "", + }; + + it("backfills dismissedInfoMessages to [] on a bare V2.1 payload", () => { + const upgraded: BlockData = migrateV2_1toV2_2(baseV2_1); + expect(upgraded.dismissedInfoMessages).toEqual([]); + }); + + it("preserves an interim-deployed dismissedInfoMessages array", () => { + const v2_1WithInterim = { + ...baseV2_1, + dismissedInfoMessages: ["already-dismissed"], + }; + const upgraded: BlockData = migrateV2_1toV2_2(v2_1WithInterim); + expect(upgraded.dismissedInfoMessages).toEqual(["already-dismissed"]); + }); + + it("runs the full V1 → V2 → V2.1 → V2.2 chain on legacy data", () => { + const v1: BlockDataV1 = { tableState, defaultBlockLabel: "Old" }; + const upgraded: BlockData = migrateV2_1toV2_2(migrateV2toV2_1(migrateV1toV2(v1))); + expect(upgraded.defaultBlockLabel).toBe("Old"); + expect(upgraded.customBlockLabel).toBe(""); + expect(upgraded.dismissedInfoMessages).toEqual([]); + }); +}); diff --git a/model/src/dataModel.ts b/model/src/dataModel.ts index 3033520..c571f15 100644 --- a/model/src/dataModel.ts +++ b/model/src/dataModel.ts @@ -1,6 +1,6 @@ import type { GraphMakerState } from "@milaboratories/graph-maker"; import { createPlDataTableStateV2, DataModelBuilder } from "@platforma-sdk/model"; -import type { BlockData, BlockDataV1, BlockDataV2 } from "./types"; +import type { BlockData, BlockDataV1, BlockDataV2, BlockDataV2_1 } from "./types"; const DEFAULT_SCATTER_STATE: GraphMakerState = { title: "Property Relationships", @@ -23,12 +23,23 @@ export const migrateV1toV2 = (v1: BlockDataV1): BlockDataV2 => ({ graphStateHistogram: { ...DEFAULT_HISTOGRAM_STATE }, }); -export const migrateV2toV2_1 = (v2: BlockDataV2): BlockData => ({ +export const migrateV2toV2_1 = (v2: BlockDataV2): BlockDataV2_1 => ({ ...v2, defaultBlockLabel: v2.defaultBlockLabel ?? "", customBlockLabel: v2.customBlockLabel ?? "", }); +// Backfills the persisted-dismissal list. `?? []` preserves any value an +// interim deployment may have written; missing → empty array. The UI +// filters info-alert strings via Set membership, so empty array means +// "show all messages". +export const migrateV2_1toV2_2 = ( + v2_1: BlockDataV2_1 & { dismissedInfoMessages?: string[] }, +): BlockData => ({ + ...v2_1, + dismissedInfoMessages: v2_1.dismissedInfoMessages ?? [], +}); + export const blockDataModel = new DataModelBuilder() .from("Ver_2026_04_28") // Already-deployed step. Future field additions must go into a new step @@ -39,11 +50,13 @@ export const blockDataModel = new DataModelBuilder() // interim-deployed value; missing fields default to "". The args // projection (resolveTraceLabel in label.ts) requires both fields to be // strings, never undefined. - .migrate("Ver_2026_05_18", migrateV2toV2_1) + .migrate("Ver_2026_05_18", migrateV2toV2_1) + .migrate("Ver_2026_05_27", migrateV2_1toV2_2) .init(() => ({ tableState: createPlDataTableStateV2(), defaultBlockLabel: "", customBlockLabel: "", graphStateScatter: { ...DEFAULT_SCATTER_STATE }, graphStateHistogram: { ...DEFAULT_HISTOGRAM_STATE }, + dismissedInfoMessages: [], })); diff --git a/model/src/label.test.ts b/model/src/label.test.ts index 1232efd..d47c4c3 100644 --- a/model/src/label.test.ts +++ b/model/src/label.test.ts @@ -25,6 +25,7 @@ const base: Omit = { template: "bins", title: "Property Distribution", } as BlockData["graphStateHistogram"], + dismissedInfoMessages: [], }; const make = (custom: string, def: string): BlockData => ({ From 36525ef374773f4f1b8431445ef602c24f3c01da Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 09:07:40 -0700 Subject: [PATCH 03/10] feat: closeable info alerts with persistent dismissal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PlAlert on the Main tab now renders a close button. Closing pushes the message string into BlockData.dismissedInfoMessages — persisted server-side, syncs across clients, survives project reopens. A computed filter excludes dismissed strings from the alert v-for. Dismissals are keyed by exact message content; workflow info.messages are deterministic per input, so the string itself is a stable key. New advisories from different inputs still appear. --- ui/src/app.ts | 1 + ui/src/pages/MainPage.vue | 29 ++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/ui/src/app.ts b/ui/src/app.ts index 4242d95..2304082 100644 --- a/ui/src/app.ts +++ b/ui/src/app.ts @@ -7,6 +7,7 @@ import ScatterPage from "./pages/ScatterPage.vue"; export const sdkPlugin = defineAppV3(platforma, (app) => { app.model.data.customBlockLabel ??= ""; + app.model.data.dismissedInfoMessages ??= []; watchEffect(() => { const anchor = app.model.data.inputAnchor; diff --git a/ui/src/pages/MainPage.vue b/ui/src/pages/MainPage.vue index 075aef3..99c22da 100644 --- a/ui/src/pages/MainPage.vue +++ b/ui/src/pages/MainPage.vue @@ -11,7 +11,7 @@ import { PlSlideModal, usePlDataTableSettingsV2, } from "@platforma-sdk/ui-vue"; -import { ref, watch } from "vue"; +import { computed, ref, watch } from "vue"; import { useApp } from "../app"; const app = useApp(); @@ -35,6 +35,26 @@ function setInput(ref?: PlRef) { app.model.data.tableState = createPlDataTableStateV2(); } +// Filtered alert list — exclude messages the user has explicitly closed. +// `data.dismissedInfoMessages` is the source of truth (persisted); the +// computed is read-only derivation. Not a hairpin: no watcher writes back +// to data from outputs. +const visibleInfoMessages = computed(() => { + const dismissed = new Set(app.model.data.dismissedInfoMessages); + return (app.model.outputs.info?.messages ?? []).filter((m) => !dismissed.has(m)); +}); + +// User-gesture write — invoked only from PlAlert's close-button emit. +// Duplicate guard avoids redundant patches if the same close event fires +// twice (defensive; PlAlert emits once, but the data array survives +// across reactive cycles). +function dismiss(message: string) { + const cur = app.model.data.dismissedInfoMessages; + if (!cur.includes(message)) { + app.model.data.dismissedInfoMessages = [...cur, message]; + } +} + const tableSettings = usePlDataTableSettingsV2({ model: () => app.model.outputs.propertiesTable, }); @@ -62,9 +82,12 @@ const tableSettings = usePlDataTableSettingsV2({ {{ message }} From 1d7e1e737fc3cb3ce54e1b1993597e109691a24a Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 09:11:27 -0700 Subject: [PATCH 04/10] feat: Settings-modal reset for dismissed info messages Adds a PlBtnGhost in the Settings slide-modal to clear dismissedInfoMessages. Disabled when the array is empty. This is the always-on reset path that remains available even if the provisional footer/badge (next commit) is dropped after manual testing. --- ui/src/pages/MainPage.vue | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ui/src/pages/MainPage.vue b/ui/src/pages/MainPage.vue index 99c22da..88ef621 100644 --- a/ui/src/pages/MainPage.vue +++ b/ui/src/pages/MainPage.vue @@ -55,6 +55,12 @@ function dismiss(message: string) { } } +const hasDismissals = computed(() => app.model.data.dismissedInfoMessages.length > 0); + +function resetDismissedInfoMessages() { + app.model.data.dismissedInfoMessages = []; +} + const tableSettings = usePlDataTableSettingsV2({ model: () => app.model.outputs.propertiesTable, }); @@ -114,6 +120,9 @@ const tableSettings = usePlDataTableSettingsV2({ Peptide extraction or MiXCR clonotyping output. Modality is auto-detected. + + Reset dismissed info messages + From 6e9981450b9c6baf857ecdce5107371f9c37ed43 Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 09:15:29 -0700 Subject: [PATCH 05/10] feat: provisional dismissed-info footer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a small footer below the PlAlert list — "N info messages hidden — Show all" — for in-context discoverability of the dismissed state. Self-contained fragment + scoped CSS; can be removed in one commit if manual testing finds it inconsistent with block design conventions. The Settings-modal reset remains the always-on path. --- ui/src/pages/MainPage.vue | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ui/src/pages/MainPage.vue b/ui/src/pages/MainPage.vue index 88ef621..e0173c7 100644 --- a/ui/src/pages/MainPage.vue +++ b/ui/src/pages/MainPage.vue @@ -57,6 +57,8 @@ function dismiss(message: string) { const hasDismissals = computed(() => app.model.data.dismissedInfoMessages.length > 0); +const hiddenInfoCount = computed(() => app.model.data.dismissedInfoMessages.length); + function resetDismissedInfoMessages() { app.model.data.dismissedInfoMessages = []; } @@ -98,6 +100,17 @@ const tableSettings = usePlDataTableSettingsV2({ {{ message }} + + + + + + + From 1c402f0c2f06063091a2a4b9dcedaf8ed6243951 Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 09:18:39 -0700 Subject: [PATCH 06/10] chore: changeset for dismissable info messages --- .changeset/dismissable-info-messages.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .changeset/dismissable-info-messages.md diff --git a/.changeset/dismissable-info-messages.md b/.changeset/dismissable-info-messages.md new file mode 100644 index 0000000..689d258 --- /dev/null +++ b/.changeset/dismissable-info-messages.md @@ -0,0 +1,16 @@ +--- +'@platforma-open/milaboratories.sequence-properties.model': minor +'@platforma-open/milaboratories.sequence-properties.ui': minor +'@platforma-open/milaboratories.sequence-properties': minor +--- + +Closeable info messages on the Main tab. The advisory alerts emitted by +the workflow (VHH detection, partial-region inputs, peptide-instability +floor, etc.) now show a close button. Dismissals persist in +`BlockData.dismissedInfoMessages` — server-side, across project reopens +and clients. The Settings modal includes a "Reset dismissed info +messages" action; a small "N hidden — show all" footer surfaces the +dismissed state in-context. + +Model schema: new `Ver_2026_05_27` migration step backfills +`dismissedInfoMessages: []` on existing projects. From 7fd35fc100a77201e13403751928569abedad82f Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 10:05:57 -0700 Subject: [PATCH 07/10] revert: drop provisional dismissed-info footer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manual testing showed the "N hidden — Show all" footer didn't fit the block's design idiom. Removing the fragment, scoped CSS, and the hiddenInfoCount computed. The Settings-modal "Reset dismissed info messages" action remains as the single reset path. Changeset body updated to match the shipped behavior. --- .changeset/dismissable-info-messages.md | 3 +-- ui/src/pages/MainPage.vue | 27 ------------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/.changeset/dismissable-info-messages.md b/.changeset/dismissable-info-messages.md index 689d258..e31847c 100644 --- a/.changeset/dismissable-info-messages.md +++ b/.changeset/dismissable-info-messages.md @@ -9,8 +9,7 @@ the workflow (VHH detection, partial-region inputs, peptide-instability floor, etc.) now show a close button. Dismissals persist in `BlockData.dismissedInfoMessages` — server-side, across project reopens and clients. The Settings modal includes a "Reset dismissed info -messages" action; a small "N hidden — show all" footer surfaces the -dismissed state in-context. +messages" action to clear all dismissals at once. Model schema: new `Ver_2026_05_27` migration step backfills `dismissedInfoMessages: []` on existing projects. diff --git a/ui/src/pages/MainPage.vue b/ui/src/pages/MainPage.vue index e0173c7..88ef621 100644 --- a/ui/src/pages/MainPage.vue +++ b/ui/src/pages/MainPage.vue @@ -57,8 +57,6 @@ function dismiss(message: string) { const hasDismissals = computed(() => app.model.data.dismissedInfoMessages.length > 0); -const hiddenInfoCount = computed(() => app.model.data.dismissedInfoMessages.length); - function resetDismissedInfoMessages() { app.model.data.dismissedInfoMessages = []; } @@ -100,17 +98,6 @@ const tableSettings = usePlDataTableSettingsV2({ {{ message }} - - - - - - - From 5f2792a8f76660e88a6dce2b300722f3ed7b135b Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 10:10:21 -0700 Subject: [PATCH 08/10] chore: bump @platforma-sdk/block-tools to 2.9.2 CI rejects PRs unless the block-tools catalog pin matches the latest release. Includes drift from pnpm-lock.yaml updating peer-dep annotations to match the resolved typescript version. --- pnpm-lock.yaml | 48 ++++++++++++++++++++++----------------------- pnpm-workspace.yaml | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 161cf40..6aaec8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,8 +25,8 @@ catalogs: specifier: 1.8.2 version: 1.8.2 '@platforma-sdk/block-tools': - specifier: 2.8.1 - version: 2.8.1 + specifier: 2.9.2 + version: 2.9.2 '@platforma-sdk/eslint-config': specifier: 1.2.0 version: 1.2.0 @@ -86,7 +86,7 @@ importers: version: 1.4.0(@types/node@25.3.2)(rollup@4.53.3)(vue@3.5.25(typescript@5.9.3))(yaml@2.8.1) '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.8.1 + version: 2.9.2 shx: specifier: 'catalog:' version: 0.4.0 @@ -111,7 +111,7 @@ importers: devDependencies: '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.8.1 + version: 2.9.2 model: dependencies: @@ -139,7 +139,7 @@ importers: version: 1.2.3 '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.8.1 + version: 2.9.2 vitest: specifier: 'catalog:' version: 4.1.4(@types/node@25.3.2)(@vitest/coverage-istanbul@4.1.4)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1)) @@ -1073,8 +1073,8 @@ packages: resolution: {integrity: sha512-eVDnXExhKB4DYzqkWD49MYyi6AyBxWyG8WoDMFrB23FjyHgMTR7rSoep0iUsWEoLLGnwq4Qd8l89/hBYpAVg0A==} engines: {node: '>=22.19.0'} - '@milaboratories/pl-client@3.8.0': - resolution: {integrity: sha512-ADUFHvwtGDC/sOYd4btCw2GdR58gq3+Nm9yV3UUYhmH5dv3J/1tXsxJH15mv1mT9YvK0IS4vMw0eDteeQUmaiQ==} + '@milaboratories/pl-client@3.9.2': + resolution: {integrity: sha512-Bv8TvriImpt8LKPL5qIolHULKjHZfn8rQHXS//0k5wOpgSR32V6UtVi4kmtkK3xsLcXodmtl1inP2fd1Pwdpig==} engines: {node: '>=22.19.0'} '@milaboratories/pl-config@1.8.1': @@ -1107,8 +1107,8 @@ packages: '@milaboratories/pl-model-backend@1.2.29': resolution: {integrity: sha512-0jL+k6z6MJha0XMFXq/e814THyvNZZNeBB6zcLYtiWKVYRCKp/iK43xOFCzGAgT1uQVz4AdhKsqgqQv5B9deBw==} - '@milaboratories/pl-model-backend@1.3.1': - resolution: {integrity: sha512-zDWTa/AqI2YSmD3FpQavWI9OnZzMRoNQS+E8YtdSadC9LkvetqY5YI9Uu/IAGrWVonlvjD6uA0PrPX5hACOxow==} + '@milaboratories/pl-model-backend@1.3.5': + resolution: {integrity: sha512-9SWpcZg98crSx6hArAncaEqlvo3AA+ZoA4mZsZSQ7P9tEOGSmQfMTo9WuNbudkCkiAuZm6Xsxv1sKTO4ojtx+g==} '@milaboratories/pl-model-common@1.31.1': resolution: {integrity: sha512-MLQvhXXFOykABZr8aVgzt5x0htT7ye4cvvnVy0TgOITXpct+lEmWvaVj29zirK/0VFw7wnTXDGnLwusr77NZFA==} @@ -1134,8 +1134,8 @@ packages: '@milaboratories/pl-model-middle-layer@1.19.4': resolution: {integrity: sha512-2SzgfHmTpSewPAv3WqbS6XHpObh69Mull3b1ec2bhn11ij6zSEDcBrMz0fFQ1XViAVQcafC4CjNTDZ/6s92kgQ==} - '@milaboratories/pl-model-middle-layer@1.20.0': - resolution: {integrity: sha512-RGEJD0avNEBejl1SjCqn7RWNiK15xCNWMXfNziiHUcG4n+QxYm1sk5AezfWsKE3j3y1HdzZnYaoGto9Z1RoV7A==} + '@milaboratories/pl-model-middle-layer@1.22.0': + resolution: {integrity: sha512-MvdNkgPDaAoHnVU3IeeyGfZ9SynJwjsYKnvxNi3YQ9BzztwCDmSwrDijsVvo7lUBoeqLiXvLSr9ug98frKF6yg==} '@milaboratories/pl-tree@1.11.0': resolution: {integrity: sha512-L6GYK0fff9ZsuZcuKW1wbu7uJl4I1S6zRhp4bpcnuCcLZuo3Qqf3zvmvax2bAl7e/rsRa6cMhz409PXS0c5Cww==} @@ -1626,8 +1626,8 @@ packages: resolution: {integrity: sha512-7msHgkgr3uFTitgokcOFAqdO9mtAUr9rHnmjk26WzIzO1HY/uyrs2AHWpdc/skchJHr1U7HHzNt7oQ3RdzZWpQ==} hasBin: true - '@platforma-sdk/block-tools@2.8.1': - resolution: {integrity: sha512-5PA8iAdndsxlbk4YMAJDnueFBNracOHPDV8a6Zw10mkxP6+YFvsnlA4+kJoh7ULTgI6qPpLmCYwvPdA6KFwd7g==} + '@platforma-sdk/block-tools@2.9.2': + resolution: {integrity: sha512-DXtZsma9aA5PBRaQzXqf79UMDo07NJrKZb/m4aaio7cxIG2ASG+CEgAkphzkeDcauMMRQ+o1tBQ2g9IptQvTAQ==} hasBin: true '@platforma-sdk/blocks-deps-updater@2.2.0': @@ -8144,7 +8144,7 @@ snapshots: utility-types: 3.11.0 yaml: 2.8.1 - '@milaboratories/pl-client@3.8.0': + '@milaboratories/pl-client@3.9.2': dependencies: '@grpc/grpc-js': 1.13.4 '@milaboratories/pl-http': 1.2.4 @@ -8267,9 +8267,9 @@ snapshots: canonicalize: 2.1.0 zod: 3.25.76 - '@milaboratories/pl-model-backend@1.3.1': + '@milaboratories/pl-model-backend@1.3.5': dependencies: - '@milaboratories/pl-client': 3.8.0 + '@milaboratories/pl-client': 3.9.2 canonicalize: 2.1.0 zod: 3.25.76 @@ -8333,7 +8333,7 @@ snapshots: utility-types: 3.11.0 zod: 3.25.76 - '@milaboratories/pl-model-middle-layer@1.20.0': + '@milaboratories/pl-model-middle-layer@1.22.0': dependencies: '@milaboratories/helpers': 1.14.2 '@milaboratories/pl-model-common': 1.42.0 @@ -8378,7 +8378,7 @@ snapshots: oxfmt: 0.35.0 oxlint: 1.43.0 rolldown: 1.0.0-rc.16 - rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.6.3)) + rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3)) rollup-plugin-copy: 3.5.0 rollup-plugin-sourcemaps2: 0.5.6(@types/node@25.3.2)(rollup@4.53.3) typescript: 5.9.3 @@ -8418,7 +8418,7 @@ snapshots: oxfmt: 0.35.0 oxlint: 1.43.0 rolldown: 1.0.0-rc.16 - rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.6.3)) + rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3)) rollup-plugin-copy: 3.5.0 rollup-plugin-sourcemaps2: 0.5.6(@types/node@25.3.2)(rollup@4.53.3) typescript: 5.9.3 @@ -8458,7 +8458,7 @@ snapshots: oxfmt: 0.35.0 oxlint: 1.43.0 rolldown: 1.0.0-rc.16 - rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.6.3)) + rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3)) rollup-plugin-copy: 3.5.0 rollup-plugin-sourcemaps2: 0.5.6(@types/node@25.3.2)(rollup@4.53.3) typescript: 5.9.3 @@ -9020,13 +9020,13 @@ snapshots: transitivePeerDependencies: - aws-crt - '@platforma-sdk/block-tools@2.8.1': + '@platforma-sdk/block-tools@2.9.2': dependencies: '@aws-sdk/client-s3': 3.859.0 '@milaboratories/pl-http': 1.2.4 - '@milaboratories/pl-model-backend': 1.3.1 + '@milaboratories/pl-model-backend': 1.3.5 '@milaboratories/pl-model-common': 1.42.0 - '@milaboratories/pl-model-middle-layer': 1.20.0 + '@milaboratories/pl-model-middle-layer': 1.22.0 '@milaboratories/resolve-helper': 1.1.3 '@milaboratories/ts-helpers': 1.8.2 '@milaboratories/ts-helpers-oclif': 1.1.41 @@ -14446,7 +14446,7 @@ snapshots: dependencies: glob: 10.4.5 - rolldown-plugin-dts@0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.6.3)): + rolldown-plugin-dts@0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3)): dependencies: '@babel/generator': 8.0.0-rc.3 '@babel/helper-validator-identifier': 8.0.0-rc.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 59b43ec..a74ba28 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,7 +11,7 @@ catalog: "@milaboratories/ts-builder": 1.4.0 "@milaboratories/ts-configs": 1.2.3 "@platforma-sdk/workflow-tengo": 5.24.0 - "@platforma-sdk/block-tools": 2.8.1 + "@platforma-sdk/block-tools": 2.9.2 "@platforma-sdk/model": 1.77.0 "@platforma-sdk/ui-vue": 1.77.0 "@platforma-sdk/test": 1.77.1 From 10e00ef5890e77bced9eca323a7dca7b649d590c Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 10:33:37 -0700 Subject: [PATCH 09/10] chore: bump @platforma-sdk/tengo-builder to 3.0.5 CI flags the 2.5.29 pin as outdated. Build is clean against the new major version; no workflow code touched on this branch so the bump is catalog-only. --- pnpm-lock.yaml | 22 +++++++++++----------- pnpm-workspace.yaml | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6aaec8c..411afaf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,8 +37,8 @@ catalogs: specifier: 3.12.0 version: 3.12.0 '@platforma-sdk/tengo-builder': - specifier: 2.5.29 - version: 2.5.29 + specifier: 3.0.5 + version: 3.0.5 '@platforma-sdk/test': specifier: 1.77.1 version: 1.77.1 @@ -250,7 +250,7 @@ importers: devDependencies: '@platforma-sdk/tengo-builder': specifier: 'catalog:' - version: 2.5.29 + version: 3.0.5 '@platforma-sdk/test': specifier: 'catalog:' version: 1.77.1(@bytecodealliance/preview2-shim@0.17.8)(@types/node@25.3.2)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1)) @@ -1659,8 +1659,8 @@ packages: resolution: {integrity: sha512-bId52YqV5iLDAn9D9C3xaLD1bKyymGpHe2CDEZTqV+1yWCBh1RM6iH96JIXudVJdcmJaqwJWDw6X4WzStE6SCg==} hasBin: true - '@platforma-sdk/tengo-builder@2.5.29': - resolution: {integrity: sha512-iurwyuFCq1DynPuoud182Ebdrk8dhSjLddkOSvPQb1QZ+HVU/CcEfIx0SgZ+qOLstHQ1lB2YeHv7GCaMHsjI9A==} + '@platforma-sdk/tengo-builder@3.0.5': + resolution: {integrity: sha512-9RAd9nQrs3U5C0Ro43atN3n0pvNIbyDzJzoIF/UV569ic6NwowsxUBUgazgEczchH+4tVRMax32mGgWQGg/jIw==} engines: {node: '>=22'} hasBin: true @@ -8378,7 +8378,7 @@ snapshots: oxfmt: 0.35.0 oxlint: 1.43.0 rolldown: 1.0.0-rc.16 - rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3)) + rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.6.3)) rollup-plugin-copy: 3.5.0 rollup-plugin-sourcemaps2: 0.5.6(@types/node@25.3.2)(rollup@4.53.3) typescript: 5.9.3 @@ -8418,7 +8418,7 @@ snapshots: oxfmt: 0.35.0 oxlint: 1.43.0 rolldown: 1.0.0-rc.16 - rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3)) + rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.6.3)) rollup-plugin-copy: 3.5.0 rollup-plugin-sourcemaps2: 0.5.6(@types/node@25.3.2)(rollup@4.53.3) typescript: 5.9.3 @@ -8458,7 +8458,7 @@ snapshots: oxfmt: 0.35.0 oxlint: 1.43.0 rolldown: 1.0.0-rc.16 - rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3)) + rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.6.3)) rollup-plugin-copy: 3.5.0 rollup-plugin-sourcemaps2: 0.5.6(@types/node@25.3.2)(rollup@4.53.3) typescript: 5.9.3 @@ -9112,9 +9112,9 @@ snapshots: transitivePeerDependencies: - aws-crt - '@platforma-sdk/tengo-builder@2.5.29': + '@platforma-sdk/tengo-builder@3.0.5': dependencies: - '@milaboratories/pl-model-backend': 1.2.29 + '@milaboratories/pl-model-backend': 1.3.5 '@milaboratories/resolve-helper': 1.1.3 '@milaboratories/tengo-tester': 1.6.4 '@milaboratories/ts-helpers': 1.8.2 @@ -14446,7 +14446,7 @@ snapshots: dependencies: glob: 10.4.5 - rolldown-plugin-dts@0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3)): + rolldown-plugin-dts@0.23.2(rolldown@1.0.0-rc.16)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.6.3)): dependencies: '@babel/generator': 8.0.0-rc.3 '@babel/helper-validator-identifier': 8.0.0-rc.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a74ba28..ec1e236 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,7 +16,7 @@ catalog: "@platforma-sdk/ui-vue": 1.77.0 "@platforma-sdk/test": 1.77.1 "@milaboratories/helpers": 1.14.2 - "@platforma-sdk/tengo-builder": 2.5.29 + "@platforma-sdk/tengo-builder": 3.0.5 "@platforma-sdk/package-builder": 3.12.0 "@platforma-sdk/blocks-deps-updater": 2.2.0 "@platforma-sdk/eslint-config": 1.2.0 From f7ee9d470fa993820710f2a53f1e9a4372afe875 Mon Sep 17 00:00:00 2001 From: Paul Newling Date: Wed, 27 May 2026 10:48:16 -0700 Subject: [PATCH 10/10] fix: guard PlAlert close-emit against future true value PlAlert's closeable close-button currently always emits false, so the unconditional dismiss call is correct today. Add an explicit !val guard so a future PlAlert API change (or programmatic visibility reset) can't silently move messages to the dismissed list without a user gesture. Addresses greptile-apps review comment. --- ui/src/pages/MainPage.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/src/pages/MainPage.vue b/ui/src/pages/MainPage.vue index 88ef621..9f988ab 100644 --- a/ui/src/pages/MainPage.vue +++ b/ui/src/pages/MainPage.vue @@ -93,7 +93,11 @@ const tableSettings = usePlDataTableSettingsV2({ type="info" closeable :model-value="true" - @update:model-value="() => dismiss(message)" + @update:model-value=" + (val) => { + if (!val) dismiss(message); + } + " > {{ message }}