From 787f2961c2ccc01d46618a6712e70cedea99a410 Mon Sep 17 00:00:00 2001 From: Tao Schiro Date: Mon, 21 Sep 2026 11:59:47 +0200 Subject: [PATCH 1/8] fix(LoginAndExtensionLoading): Changes UI feedback and add loaders to extension page --- internal/utils/api_fetch.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/internal/utils/api_fetch.ts b/internal/utils/api_fetch.ts index 5d1b35db..f11bc88d 100644 --- a/internal/utils/api_fetch.ts +++ b/internal/utils/api_fetch.ts @@ -33,6 +33,7 @@ interface FetchErrorResponseLike { description?: string; } +// oxlint-disable-next-line max-lines-per-function function api_fetch( microservice: Microservice, { schema, params = {}, headers = {} }: ApiFetchParams, @@ -41,7 +42,8 @@ function api_fetch( response_function, response_error_function, timeout, - }: RequestHandlersWithValidation & { timeout?: number } = {}, + skip_feedback_error, + }: RequestHandlersWithValidation & { timeout?: number; skip_feedback_error?: boolean } = {}, ) { console.log("[API] Fetching", microservice.base_url); const feedbackStore = useFeedbackStore(); @@ -60,12 +62,14 @@ function api_fetch( request_error_function(error: unknown) { microservice.stop_request(); const typedError = error as FetchErrorLike; - feedbackStore.add_error( - typedError.code ?? 0, - schema.$id, - typedError.message ?? "", - typedError.stack ?? "", - ); + if (!skip_feedback_error) { + feedbackStore.add_error( + typedError.code ?? 0, + schema.$id, + typedError.message ?? "", + typedError.stack ?? "", + ); + } if (request_error_function) { request_error_function(error); } @@ -80,12 +84,14 @@ function api_fetch( response_error_function(response: unknown) { microservice.stop_request(); const typedResponse = response as FetchErrorResponseLike; - feedbackStore.add_error( - typedResponse.status ?? 0, - schema.$id, - typedResponse.name ?? "", - typedResponse.description ?? "", - ); + if (!skip_feedback_error) { + feedbackStore.add_error( + typedResponse.status ?? 0, + schema.$id, + typedResponse.name ?? "", + typedResponse.description ?? "", + ); + } if (response_error_function) { response_error_function(response); } From cde609cbdd9e9e7063911909932ddef79368370f Mon Sep 17 00:00:00 2001 From: Tao Schiro Date: Tue, 22 Sep 2026 11:06:50 +0200 Subject: [PATCH 2/8] fix oxlint --- app/components/csv-preview/CsvPreviewer.vue | 46 ++++++++++----------- app/stores/app.ts | 25 ++++++----- app/stores/viewer.ts | 16 +++---- internal/stores/hybrid_viewer/filters.ts | 10 +---- internal/stores/hybrid_viewer/viewport.ts | 10 +---- 5 files changed, 47 insertions(+), 60 deletions(-) diff --git a/app/components/csv-preview/CsvPreviewer.vue b/app/components/csv-preview/CsvPreviewer.vue index c62b6fd1..b2f81b92 100644 --- a/app/components/csv-preview/CsvPreviewer.vue +++ b/app/components/csv-preview/CsvPreviewer.vue @@ -77,6 +77,27 @@ function autoDetectSeparator(content: string): string { return best; } +function splitLine(line: string, sep: string): string[] { + if (!sep) { + return [line]; + } + const result = []; + let current = ""; + let inQuotes = false; + for (const char of line) { + if (char === '"') { + inQuotes = !inQuotes; + } else if (char === sep && !inQuotes) { + result.push(current.trim()); + current = ""; + } else { + current += char; + } + } + result.push(current.trim()); + return result; +} + function parseContent(): string[] { if (!rawContent.value) { return []; @@ -84,29 +105,8 @@ function parseContent(): string[] { const allLines = rawContent.value.split(/\r?\n/u).filter((line) => line.trim() !== ""); - function splitLine(line: string): string[] { - if (!separator.value) { - return [line]; - } - const result = []; - let current = ""; - let inQuotes = false; - for (const char of line) { - if (char === '"') { - inQuotes = !inQuotes; - } else if (char === separator.value && !inQuotes) { - result.push(current.trim()); - current = ""; - } else { - current += char; - } - } - result.push(current.trim()); - return result; - } - const headerLine = allLines[headerRow.value]; - const rawHeaders = headerLine ? splitLine(headerLine) : []; + const rawHeaders = headerLine ? splitLine(headerLine, separator.value) : []; previewHeaders.value = rawHeaders.map((header, index) => ({ title: header || `Column ${index + 1}`, @@ -117,7 +117,7 @@ function parseContent(): string[] { const dataLines = allLines.slice(firstRow.value, firstRow.value + PREVIEW_ROWS_LIMIT); previewRows.value = dataLines.map((line) => { - const row = splitLine(line); + const row = splitLine(line, separator.value); const obj: CsvRow = {}; for (let index = 0; index < row.length; index += 1) { obj[`col${index}`] = row[index] ?? ""; diff --git a/app/stores/app.ts b/app/stores/app.ts index 910d06b8..d9d83573 100644 --- a/app/stores/app.ts +++ b/app/stores/app.ts @@ -141,14 +141,22 @@ export const useAppStore = defineStore("app", () => { setExtensionEnabled, getExtensionEnabled, } = useAppExtensions(); + const request_counter = ref(0); + function start_request(): void { + request_counter.value += 1; + } + function stop_request(): void { + request_counter.value -= 1; + } + const is_busy = computed(() => request_counter.value > 0); async function upload(file: Readonly, callbacks: RequestHandlers = {}): Promise { - const store = useAppStore(); + const microservice = { $id: "app", base_url: base_url.value, start_request, stop_request }; const schema = opengeodeweb_front_schemas.api.local.extensions.upload; const { PROJECT: projectName } = useRuntimeConfig().public; const params = { projectName }; const result = await upload_file( - store, + microservice, { schema, file, params }, { ...callbacks, @@ -170,9 +178,9 @@ export const useAppStore = defineStore("app", () => { }: Readonly<{ schema: JsonRpcSchema; params?: Readonly> }>, callbacks: RequestHandlers = {}, ): Promise { - const store = useAppStore(); + const microservice = { $id: "app", base_url: base_url.value, start_request, stop_request }; const result = await api_fetch( - store, + microservice, // The app store is only ever used with HTTP ("front") schemas, which always carry `methods`; the wider JsonRpcSchema param above is kept as-is to match this action's public signature (e.g. relayed from get_version-style callers that only know about the shared, looser schema shape). // oxlint-disable-next-line no-unsafe-type-assertion -- narrowing optional `methods` to required is safe here; see comment above. { schema: schema as JsonRpcSchema & { methods: string[] }, params }, @@ -189,15 +197,6 @@ export const useAppStore = defineStore("app", () => { return result as TResult; } - const request_counter = ref(0); - function start_request(): void { - request_counter.value += 1; - } - function stop_request(): void { - request_counter.value -= 1; - } - const is_busy = computed(() => request_counter.value > 0); - const projectFolderPath = ref(""); async function createProjectFolder(): Promise { diff --git a/app/stores/viewer.ts b/app/stores/viewer.ts index 51dcbf73..f73140e2 100644 --- a/app/stores/viewer.ts +++ b/app/stores/viewer.ts @@ -58,6 +58,12 @@ export const useViewerStore = defineStore( viewer_url += "/ws"; return viewer_url; }); + function start_request(): void { + request_counter.value += 1; + } + function stop_request(): void { + request_counter.value -= 1; + } const is_busy = computed(() => request_counter.value > 0); function toggle_picking_mode(value: boolean): void { picking_mode.value = value; @@ -74,9 +80,9 @@ export const useViewerStore = defineStore( }>, callbacks: RequestHandlers = {}, ): Promise { - const store = useViewerStore(); + const microservice = { $id: "viewer", base_url: base_url.value, start_request, stop_request }; const result = await viewer_call( - store, + microservice, { schema, params, @@ -147,12 +153,6 @@ export const useViewerStore = defineStore( } }); } - function start_request(): void { - request_counter.value += 1; - } - function stop_request(): void { - request_counter.value -= 1; - } async function launch(args: Readonly<{ projectFolderPath?: string }> = {}): Promise { const appStore = useAppStore(); const { COMMAND_VIEWER, NUXT_ROOT_PATH } = useRuntimeConfig().public; diff --git a/internal/stores/hybrid_viewer/filters.ts b/internal/stores/hybrid_viewer/filters.ts index d820ef69..6933dd91 100644 --- a/internal/stores/hybrid_viewer/filters.ts +++ b/internal/stores/hybrid_viewer/filters.ts @@ -34,15 +34,9 @@ function useHybridViewerFilters(): { setClippingPlanes: (ids: string[], planes: unknown) => Promise; setShrink: (ids: string[], shrink_factor: number) => Promise; } { - async function setClippingPlanes(ids: string[], planes: unknown): Promise { - await performSetClippingPlanes(ids, planes); - } - async function setShrink(ids: string[], shrink_factor: number): Promise { - await performSetShrink(ids, shrink_factor); - } return { - setClippingPlanes, - setShrink, + setClippingPlanes: performSetClippingPlanes, + setShrink: performSetShrink, }; } export { performSetClippingPlanes, performSetShrink, useHybridViewerFilters }; diff --git a/internal/stores/hybrid_viewer/viewport.ts b/internal/stores/hybrid_viewer/viewport.ts index e02e44a0..38ab6778 100644 --- a/internal/stores/hybrid_viewer/viewport.ts +++ b/internal/stores/hybrid_viewer/viewport.ts @@ -167,15 +167,9 @@ function performSetContainer(container: ContainerRef | undefined): void { } function useHybridViewerViewport(): HybridViewerViewport { const { viewStream } = useHybridViewerCore(); - function setContainer(container: ContainerRef | undefined): void { - performSetContainer(container); - } - async function resize(width: number, height: number): Promise { - await performResize(width, height); - } return { - setContainer, - resize, + setContainer: performSetContainer, + resize: performResize, viewStream, }; } From d63bd59802a883e108c8407719846d92999c5b49 Mon Sep 17 00:00:00 2001 From: Tao Schiro Date: Tue, 22 Sep 2026 11:21:24 +0200 Subject: [PATCH 3/8] update microservice --- app/stores/viewer.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/stores/viewer.ts b/app/stores/viewer.ts index f73140e2..15b9cdf6 100644 --- a/app/stores/viewer.ts +++ b/app/stores/viewer.ts @@ -80,7 +80,13 @@ export const useViewerStore = defineStore( }>, callbacks: RequestHandlers = {}, ): Promise { - const microservice = { $id: "viewer", base_url: base_url.value, start_request, stop_request }; + const microservice = { + $id: "viewer", + client: client.value, + base_url: base_url.value, + start_request, + stop_request, + }; const result = await viewer_call( microservice, { From ca03d37106d2f97fe77b73dc196538d36eaa4ba0 Mon Sep 17 00:00:00 2001 From: Tao Schiro Date: Tue, 22 Sep 2026 14:13:45 +0200 Subject: [PATCH 4/8] discard changes --- app/stores/app.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/app/stores/app.ts b/app/stores/app.ts index d9d83573..910d06b8 100644 --- a/app/stores/app.ts +++ b/app/stores/app.ts @@ -141,22 +141,14 @@ export const useAppStore = defineStore("app", () => { setExtensionEnabled, getExtensionEnabled, } = useAppExtensions(); - const request_counter = ref(0); - function start_request(): void { - request_counter.value += 1; - } - function stop_request(): void { - request_counter.value -= 1; - } - const is_busy = computed(() => request_counter.value > 0); async function upload(file: Readonly, callbacks: RequestHandlers = {}): Promise { - const microservice = { $id: "app", base_url: base_url.value, start_request, stop_request }; + const store = useAppStore(); const schema = opengeodeweb_front_schemas.api.local.extensions.upload; const { PROJECT: projectName } = useRuntimeConfig().public; const params = { projectName }; const result = await upload_file( - microservice, + store, { schema, file, params }, { ...callbacks, @@ -178,9 +170,9 @@ export const useAppStore = defineStore("app", () => { }: Readonly<{ schema: JsonRpcSchema; params?: Readonly> }>, callbacks: RequestHandlers = {}, ): Promise { - const microservice = { $id: "app", base_url: base_url.value, start_request, stop_request }; + const store = useAppStore(); const result = await api_fetch( - microservice, + store, // The app store is only ever used with HTTP ("front") schemas, which always carry `methods`; the wider JsonRpcSchema param above is kept as-is to match this action's public signature (e.g. relayed from get_version-style callers that only know about the shared, looser schema shape). // oxlint-disable-next-line no-unsafe-type-assertion -- narrowing optional `methods` to required is safe here; see comment above. { schema: schema as JsonRpcSchema & { methods: string[] }, params }, @@ -197,6 +189,15 @@ export const useAppStore = defineStore("app", () => { return result as TResult; } + const request_counter = ref(0); + function start_request(): void { + request_counter.value += 1; + } + function stop_request(): void { + request_counter.value -= 1; + } + const is_busy = computed(() => request_counter.value > 0); + const projectFolderPath = ref(""); async function createProjectFolder(): Promise { From 14ca8d2f708fdedf2d72a7bb0a67596c693d8d47 Mon Sep 17 00:00:00 2001 From: Tao Schiro Date: Tue, 22 Sep 2026 14:33:23 +0200 Subject: [PATCH 5/8] fix comments --- app/utils/log.ts | 6 +++--- internal/stores/hybrid_viewer/filters.ts | 10 +++++----- internal/stores/hybrid_viewer/viewport.ts | 12 ++++++------ internal/utils/api_fetch.ts | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/utils/log.ts b/app/utils/log.ts index 9bc9495b..a5eaf1ab 100644 --- a/app/utils/log.ts +++ b/app/utils/log.ts @@ -1,11 +1,11 @@ import type { JsonRpcSchema } from "@ogw_shared/utils/types.js"; interface Loggable { - $id?: string; + $id: string; } function startRequestLog(microservice: Readonly, schema: Readonly): Date { - console.log(`[${microservice.$id ?? "API"}] Request:`, schema.$id); + console.log(`[${microservice.$id}] Request:`, schema.$id); const requestStartingTime = new Date(Date.now()); return requestStartingTime; } @@ -17,7 +17,7 @@ function endRequestLog( ): void { const requestEndingTime = new Date(Date.now()); console.log( - `[${microservice.$id ?? "API"}] Request completed:`, + `[${microservice.$id}] Request completed:`, schema.$id, "in", requestEndingTime.getSeconds() - requestStartingTime.getSeconds(), diff --git a/internal/stores/hybrid_viewer/filters.ts b/internal/stores/hybrid_viewer/filters.ts index 6933dd91..b3bfe601 100644 --- a/internal/stores/hybrid_viewer/filters.ts +++ b/internal/stores/hybrid_viewer/filters.ts @@ -2,7 +2,7 @@ import { useHybridViewerCore } from "./core"; import { useViewerStore } from "@ogw_front/stores/viewer"; import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json"; -async function performSetClippingPlanes(ids: string[], planes: unknown): Promise { +async function setClippingPlanes(ids: string[], planes: unknown): Promise { const viewerStore = useViewerStore(); const { remoteRender } = useHybridViewerCore(); const schema = viewer_schemas.opengeodeweb_viewer.viewer.clipping_planes; @@ -16,7 +16,7 @@ async function performSetClippingPlanes(ids: string[], planes: unknown): Promise }); await remoteRender(); } -async function performSetShrink(ids: string[], shrink_factor: number): Promise { +async function setShrink(ids: string[], shrink_factor: number): Promise { const viewerStore = useViewerStore(); const { remoteRender } = useHybridViewerCore(); const schema = viewer_schemas.opengeodeweb_viewer.viewer.shrink; @@ -35,8 +35,8 @@ function useHybridViewerFilters(): { setShrink: (ids: string[], shrink_factor: number) => Promise; } { return { - setClippingPlanes: performSetClippingPlanes, - setShrink: performSetShrink, + setClippingPlanes, + setShrink, }; } -export { performSetClippingPlanes, performSetShrink, useHybridViewerFilters }; +export { setClippingPlanes, setShrink, useHybridViewerFilters }; diff --git a/internal/stores/hybrid_viewer/viewport.ts b/internal/stores/hybrid_viewer/viewport.ts index 38ab6778..cc7081a6 100644 --- a/internal/stores/hybrid_viewer/viewport.ts +++ b/internal/stores/hybrid_viewer/viewport.ts @@ -15,7 +15,7 @@ interface HybridViewerViewport { viewStream: Ref; } -async function performResize(width: number, height: number): Promise { +async function resize(width: number, height: number): Promise { const { genericRenderWindow, remoteRender, status, viewStream } = useHybridViewerCore(); const viewerStore = useViewerStore(); if (viewerStore.status !== Status.CONNECTED || status.value !== Status.CREATED) { @@ -77,7 +77,7 @@ function performClickPicking(event: MouseEvent, containerElement: HTMLElement): ); } -function performSetContainer(container: ContainerRef | undefined): void { +function setContainer(container: ContainerRef | undefined): void { if (!container || !container.value) { return; } @@ -102,7 +102,7 @@ function performSetContainer(container: ContainerRef | undefined): void { transition: "opacity 0.1s ease-in", zIndex: 1, }); - void performResize(containerElement.offsetWidth, containerElement.offsetHeight); + void resize(containerElement.offsetWidth, containerElement.offsetHeight); let has_dragged = false; useMousePressed({ target: containerElement, @@ -168,10 +168,10 @@ function performSetContainer(container: ContainerRef | undefined): void { function useHybridViewerViewport(): HybridViewerViewport { const { viewStream } = useHybridViewerCore(); return { - setContainer: performSetContainer, - resize: performResize, + setContainer, + resize, viewStream, }; } -export { performClickPicking, performResize, performSetContainer, useHybridViewerViewport }; +export { performClickPicking, resize, setContainer, useHybridViewerViewport }; export type { ViewStreamLike } from "./vtk_types"; diff --git a/internal/utils/api_fetch.ts b/internal/utils/api_fetch.ts index 78261d8b..9099a8fb 100644 --- a/internal/utils/api_fetch.ts +++ b/internal/utils/api_fetch.ts @@ -5,7 +5,7 @@ import { useFeedbackStore } from "@ogw_front/stores/feedback"; // The microservice-backed Pinia stores (back/app/...) all expose this shape; only the slice actually used here needs to be declared. interface Microservice { - $id?: string; + $id: string; base_url: string; start_request: () => void; stop_request: () => void; From e710afa61f07c74305a04d786637a09093737d7a Mon Sep 17 00:00:00 2001 From: Tao Schiro Date: Tue, 22 Sep 2026 15:13:55 +0200 Subject: [PATCH 6/8] oxlint --- app/stores/app.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/app/stores/app.ts b/app/stores/app.ts index 910d06b8..be093a03 100644 --- a/app/stores/app.ts +++ b/app/stores/app.ts @@ -142,13 +142,21 @@ export const useAppStore = defineStore("app", () => { getExtensionEnabled, } = useAppExtensions(); + const request_counter = ref(0); + function start_request(): void { + request_counter.value += 1; + } + function stop_request(): void { + request_counter.value -= 1; + } + const is_busy = computed(() => request_counter.value > 0); + async function upload(file: Readonly, callbacks: RequestHandlers = {}): Promise { - const store = useAppStore(); const schema = opengeodeweb_front_schemas.api.local.extensions.upload; const { PROJECT: projectName } = useRuntimeConfig().public; const params = { projectName }; const result = await upload_file( - store, + { $id: "app", base_url: base_url.value, start_request, stop_request }, { schema, file, params }, { ...callbacks, @@ -170,9 +178,8 @@ export const useAppStore = defineStore("app", () => { }: Readonly<{ schema: JsonRpcSchema; params?: Readonly> }>, callbacks: RequestHandlers = {}, ): Promise { - const store = useAppStore(); const result = await api_fetch( - store, + { $id: "app", base_url: base_url.value, start_request, stop_request }, // The app store is only ever used with HTTP ("front") schemas, which always carry `methods`; the wider JsonRpcSchema param above is kept as-is to match this action's public signature (e.g. relayed from get_version-style callers that only know about the shared, looser schema shape). // oxlint-disable-next-line no-unsafe-type-assertion -- narrowing optional `methods` to required is safe here; see comment above. { schema: schema as JsonRpcSchema & { methods: string[] }, params }, @@ -189,15 +196,6 @@ export const useAppStore = defineStore("app", () => { return result as TResult; } - const request_counter = ref(0); - function start_request(): void { - request_counter.value += 1; - } - function stop_request(): void { - request_counter.value -= 1; - } - const is_busy = computed(() => request_counter.value > 0); - const projectFolderPath = ref(""); async function createProjectFolder(): Promise { From bd84b177fa194e503302f4b5147fee4ca1ad2cc2 Mon Sep 17 00:00:00 2001 From: Tao Schiro Date: Wed, 23 Sep 2026 10:03:52 +0200 Subject: [PATCH 7/8] discard --- app/components/csv-preview/CsvPreviewer.vue | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/components/csv-preview/CsvPreviewer.vue b/app/components/csv-preview/CsvPreviewer.vue index 477d428e..34e644e6 100644 --- a/app/components/csv-preview/CsvPreviewer.vue +++ b/app/components/csv-preview/CsvPreviewer.vue @@ -77,8 +77,8 @@ function autoDetectSeparator(content: string): string { return best; } -function splitLine(line: string, sep: string): string[] { - if (!sep) { +function splitLine(line: string): string[] { + if (!separator.value) { return [line]; } const result = []; @@ -87,7 +87,7 @@ function splitLine(line: string, sep: string): string[] { for (const char of line) { if (char === '"') { inQuotes = !inQuotes; - } else if (char === sep && !inQuotes) { + } else if (char === separator.value && !inQuotes) { result.push(current.trim()); current = ""; } else { @@ -98,14 +98,14 @@ function splitLine(line: string, sep: string): string[] { return result; } -function parseContent(): void { +function parseContent(): string[] { if (!rawContent.value) { - return; + return []; } const allLines = rawContent.value.split(/\r?\n/u).filter((line) => line.trim() !== ""); const headerLine = allLines[headerRow.value]; - const rawHeaders = headerLine ? splitLine(headerLine, separator.value) : []; + const rawHeaders = headerLine ? splitLine(headerLine) : []; previewHeaders.value = rawHeaders.map((header, index) => ({ title: header || `Column ${index + 1}`, @@ -116,7 +116,7 @@ function parseContent(): void { const dataLines = allLines.slice(firstRow.value, firstRow.value + PREVIEW_ROWS_LIMIT); previewRows.value = dataLines.map((line) => { - const row = splitLine(line, separator.value); + const row = splitLine(line); const obj: CsvRow = {}; for (let index = 0; index < row.length; index += 1) { obj[`col${index}`] = row[index] ?? ""; From 8cfc538d49cf89070406d619795fca93fff6ca93 Mon Sep 17 00:00:00 2001 From: Tao Schiro Date: Wed, 23 Sep 2026 10:06:07 +0200 Subject: [PATCH 8/8] update fetchraw --- shared/utils/fetch_raw.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/utils/fetch_raw.ts b/shared/utils/fetch_raw.ts index a15745bf..fa3d7169 100644 --- a/shared/utils/fetch_raw.ts +++ b/shared/utils/fetch_raw.ts @@ -67,8 +67,8 @@ async function performFetch({ }, async onResponse({ response, - // oxlint-disable-next-line eslint/id-length -- mirrors the real ofetch/vitest API field name (`ok`/`fn`) }: { + // oxlint-disable-next-line eslint/id-length -- mirrors the real ofetch/vitest API field name (`ok`/`fn`) response: { ok: boolean; _data?: unknown }; }) { if (response.ok && response_function) {