@sinequa/atomic-angular 2.8.0 → 2.10.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"sinequa-atomic-angular-document-workspaces.mjs","names":[],"sources":["../../../projects/atomic-angular/document-workspaces/utils/doc-type.ts","../../../projects/atomic-angular/document-workspaces/utils/workspace-color.ts","../../../projects/atomic-angular/document-workspaces/store/workspace-mappers.ts","../../../projects/atomic-angular/document-workspaces/store/workspaces.store.ts","../../../projects/atomic-angular/document-workspaces/upload/upload-tracker.store.ts","../../../projects/atomic-angular/document-workspaces/services/current-user.service.ts","../../../projects/atomic-angular/document-workspaces/utils/base-url.ts","../../../projects/atomic-angular/document-workspaces/utils/upload-jobs.ts","../../../projects/atomic-angular/document-workspaces/utils/uuid.ts","../../../projects/atomic-angular/document-workspaces/upload/session-recovery.ts","../../../projects/atomic-angular/document-workspaces/upload/status-poller.ts","../../../projects/atomic-angular/document-workspaces/upload/tus-upload.ts","../../../projects/atomic-angular/document-workspaces/upload/upload.service.ts","../../../projects/atomic-angular/document-workspaces/services/principal-directory.service.ts","../../../projects/atomic-angular/document-workspaces/directives/dropzone.directive.ts","../../../projects/atomic-angular/document-workspaces/directives/resize-handle.directive.ts","../../../projects/atomic-angular/document-workspaces/utils/workspace-role.ts","../../../projects/atomic-angular/document-workspaces/directives/writer-only.directive.ts","../../../projects/atomic-angular/document-workspaces/utils/text-match.ts","../../../projects/atomic-angular/document-workspaces/pipes/highlight-match.pipe.ts","../../../projects/atomic-angular/document-workspaces/utils/format.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-document-labels.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-online-status.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-selection.ts","../../../projects/atomic-angular/document-workspaces/utils/text-search.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-text-search.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-workspace-filter.ts","../../../projects/atomic-angular/document-workspaces/components/shared/highlighted-text.ts","../../../projects/atomic-angular/document-workspaces/components/shared/workspace-role-tag.ts","../../../projects/atomic-angular/document-workspaces/utils/error-message.ts","../../../projects/atomic-angular/document-workspaces/components/shared/confirm.dialog.ts","../../../projects/atomic-angular/document-workspaces/utils/message.ts","../../../projects/atomic-angular/document-workspaces/components/shared/workspace-message.ts","../../../projects/atomic-angular/document-workspaces/components/shared/document-status-badge.ts","../../../projects/atomic-angular/document-workspaces/components/shared/document-type-icon.ts","../../../projects/atomic-angular/document-workspaces/components/shared/workspace-icon.ts","../../../projects/atomic-angular/document-workspaces/components/upload/upload-file-list.ts","../../../projects/atomic-angular/document-workspaces/components/upload/upload.dialog.ts","../../../projects/atomic-angular/document-workspaces/components/upload/connection-banner.ts","../../../projects/atomic-angular/document-workspaces/components/shared/activity-indicators.ts","../../../projects/atomic-angular/document-workspaces/components/upload/upload-tracker-section.ts","../../../projects/atomic-angular/document-workspaces/components/upload/upload-tracker.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-live-refresh.ts","../../../projects/atomic-angular/document-workspaces/components/list/workspace-row.ts","../../../projects/atomic-angular/document-workspaces/components/list/workspace-card.ts","../../../projects/atomic-angular/document-workspaces/components/list/workspace-list.ts","../../../projects/atomic-angular/document-workspaces/components/detail/document-card.ts","../../../projects/atomic-angular/document-workspaces/components/detail/document-row.ts","../../../projects/atomic-angular/document-workspaces/utils/members.ts","../../../projects/atomic-angular/document-workspaces/components/share/principal-avatar.ts","../../../projects/atomic-angular/document-workspaces/components/share/role-menu.ts","../../../projects/atomic-angular/document-workspaces/components/share/member-row.ts","../../../projects/atomic-angular/document-workspaces/components/share/principal-picker.ts","../../../projects/atomic-angular/document-workspaces/components/share/share.dialog.ts","../../../projects/atomic-angular/document-workspaces/components/preview/document-preview-panel.ts","../../../projects/atomic-angular/document-workspaces/components/detail/workspace-edit.dialog.ts","../../../projects/atomic-angular/document-workspaces/components/detail/workspace-detail.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-workspace-mention.ts","../../../projects/atomic-angular/document-workspaces/components/mention/has-content.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-new.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-rows.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-search.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-select-all.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-list.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-tabs.ts","../../../projects/atomic-angular/document-workspaces/components/picker/workspace-picker.ts","../../../projects/atomic-angular/document-workspaces/components/picker/workspace-picker.dialog.ts","../../../projects/atomic-angular/document-workspaces/components/picker/workspace-picker-launcher.ts","../../../projects/atomic-angular/document-workspaces/components/shell/workspaces-shell.ts"],"sourcesContent":["import type { DocumentType } from \"../models/workspace.model\";\n\n/**\n * The document DTO carries no `type`, so it is derived from the file name.\n *\n * @param filename - A document file name, e.g. `report.pdf`.\n *\n * @returns The icon family the document belongs to.\n */\nexport function getDocumentType(filename: string): DocumentType {\n const ext = filename.slice(filename.lastIndexOf(\".\")).toLowerCase();\n if (ext === \".pdf\") return \"pdf\";\n if (ext === \".csv\" || ext === \".xlsx\") return \"csv\";\n return \"doc\";\n}\n","import type { WorkspaceColor } from \"../models/workspace.model\";\n\nconst PALETTE: readonly WorkspaceColor[] = [\"default\", \"teal\", \"pink\", \"blue\", \"orange\", \"indigo\", \"green\"];\n\n/**\n * The API has no colour field, so one is derived from the workspace id: the\n * same workspace always renders with the same accent across reloads and\n * reorders, without the server knowing anything about it.\n *\n * @param id - The workspace (container) id.\n */\nexport function workspaceColorFor(id: string): WorkspaceColor {\n let hash = 0;\n for (let i = 0; i < id.length; i++) {\n hash = (hash * 31 + id.charCodeAt(i)) | 0;\n }\n return PALETTE[Math.abs(hash) % PALETTE.length];\n}\n","// Wire shape (@sinequa/atomic's Workspace/WorkspaceDocument) -> view model\n// (Workspace/WorkspaceDocument of ../models). This is the only place the API\n// vocabulary meets the UI one.\nimport type { Workspace as WireWorkspace, WorkspaceDocument as WireWorkspaceDocument } from \"@sinequa/atomic\";\nimport type { Workspace, WorkspaceDocument } from \"../models/workspace.model\";\nimport { getDocumentType } from \"../utils/doc-type\";\nimport { workspaceColorFor } from \"../utils/workspace-color\";\n\nexport function mapWireWorkspaceToWorkspace(dto: WireWorkspace): Workspace {\n return {\n id: dto.id,\n name: dto.name,\n color: workspaceColorFor(dto.id),\n description: dto.description ?? \"\",\n tags: dto.tags ?? [],\n documentCount: dto.documentCount,\n creatorId: dto.creator,\n ownerIds: dto.owners ?? [],\n readerIds: dto.readers ?? [],\n currentUserRole: dto.currentUserRole\n };\n}\n\n/** Lifecycle status of a document as the documents listing spells it. */\nexport type WireDocumentStatus = \"uploading\" | \"ready_for_indexing\" | \"sent_for_indexing\" | \"indexing\" | \"indexed\" | \"integrity_failed\" | \"failed\";\n\n// The server sends status/percent/jobId on every document, but @sinequa/atomic\n// WorkspaceDocument declares none of the three. Drop this widening once\n// atomic declares them.\ntype WireWorkspaceDocumentExt = WireWorkspaceDocument & {\n status?: WireDocumentStatus;\n percent?: number;\n jobId?: string;\n};\n\n/**\n * Server lifecycle status -> the three-state badge the views render.\n * `indexed` maps to null (terminal success, no badge), and an absent or\n * unknown status maps to undefined so the document renders with no badge at\n * all rather than a wrong one.\n */\nexport function mapDocumentStatus(status?: string): WorkspaceDocument[\"status\"] {\n switch (status) {\n case \"uploading\":\n return \"uploading\";\n case \"ready_for_indexing\":\n case \"sent_for_indexing\":\n case \"indexing\":\n return \"indexing\";\n case \"indexed\":\n return null;\n case \"integrity_failed\":\n case \"failed\":\n return \"error\";\n default:\n return undefined;\n }\n}\n\n// size/source/uploadedBy have no server equivalent — left unset for\n// server-loaded documents. status/percent/jobId, by contrast, ARE served:\n// mapping them is what lets a document keep its badge across a reload or a\n// re-open of the workspace.\nexport function mapWireDocumentToWorkspaceDocument(dto: WireWorkspaceDocument): WorkspaceDocument {\n const wire = dto as WireWorkspaceDocumentExt;\n return {\n id: dto.id,\n name: dto.name,\n workspaceId: dto.workspaceId,\n type: getDocumentType(dto.name),\n // The swagger makes it nullable (\"or `null` when unknown\") although atomic\n // declares a plain string: normalise so the model's optional means \"unknown\".\n createdAt: dto.createdAt ?? undefined,\n status: mapDocumentStatus(wire.status),\n percent: wire.percent,\n jobId: wire.jobId\n };\n}\n","import { computed } from \"@angular/core\";\nimport { withDevtools } from \"@angular-architects/ngrx-toolkit\";\nimport { patchState, signalStore, withComputed, withMethods, withState } from \"@ngrx/signals\";\nimport {\n addWorkspaceDocuments,\n createWorkspace,\n deleteWorkspaceDocuments,\n deleteWorkspaces,\n getWorkspace,\n listWorkspaceDocuments,\n listWorkspaces,\n updateWorkspace\n} from \"@sinequa/atomic\";\nimport type { UpdateWorkspaceInput } from \"@sinequa/atomic\";\nimport type { Workspace, WorkspaceDocument } from \"../models/workspace.model\";\nimport { mapWireDocumentToWorkspaceDocument, mapWireWorkspaceToWorkspace } from \"./workspace-mappers\";\n\n/** API maximum page size. */\nconst PAGE_SIZE = 100;\n\n/**\n * Most workspaces the list keeps loaded: \"load more\" stops there, so the list never puts an\n * unbounded number of cards in the DOM. Past it, {@link WorkspacesStore.truncated} is true.\n */\nexport const MAX_LOADED_WORKSPACES = 3 * PAGE_SIZE;\n\nexport interface Pagination {\n page: number;\n total: number;\n}\n\nexport interface WorkspacesState {\n workspaces: Workspace[];\n /** True once the first page of workspaces has been read from the server. */\n loaded: boolean;\n /** True while a (re)load is in flight. */\n loading: boolean;\n pagination: Pagination;\n documents: Record<string, WorkspaceDocument[]>;\n documentPagination: Record<string, Pagination>;\n /** Ids ticked in the \"+\"/\"@\" mention pickers — shared between every picker instance. */\n selectedIds: string[];\n}\n\nexport interface CreateWorkspaceInput {\n name: string;\n description?: string;\n tags?: string[];\n /** Optional: they go out on the same POST as the metadata, so sharing at creation costs no extra round trip. */\n owners?: string[];\n readers?: string[];\n}\n\nexport interface RemoveWorkspacesResult {\n succeeded: string[];\n failed: { id: string; error: string }[];\n}\n\nconst initialState: WorkspacesState = {\n workspaces: [],\n loaded: false,\n loading: false,\n pagination: { page: 0, total: 0 },\n documents: {},\n documentPagination: {},\n selectedIds: []\n};\n\n/** Returns `workspaces` with the document count of `id` moved by `delta`. */\nfunction withDocumentCountAdjusted(workspaces: Workspace[], id: string, delta: number): Workspace[] {\n return workspaces.map(w => (w.id === id ? { ...w, documentCount: Math.max(0, w.documentCount + delta) } : w));\n}\n\n/**\n * Workspaces and their documents, backed by the container-management API\n * (`@sinequa/atomic`'s `*Container*` functions).\n *\n * Root-scoped on purpose: the workspace list, the mention picker and the\n * save/move picker are separate components that have to see the same cache and\n * the same selection.\n *\n * Every write method resolves or rejects around the matching endpoint and only\n * touches the cache once the server has answered, so a rejected call leaves\n * the state as it was — the call site reports the error.\n *\n * `addDocuments`, `discardDocuments` and `patchDocument` stay purely local. The upload\n * flow no longer uses them: the list is the index, and only the listing writes to it.\n */\nexport const WorkspacesStore = signalStore(\n { providedIn: \"root\" },\n withDevtools(\"Workspaces\"),\n\n withState(initialState),\n\n withComputed(({ workspaces, pagination, selectedIds }) => ({\n hasMore: computed(() => workspaces().length < pagination().total && workspaces().length < MAX_LOADED_WORKSPACES),\n /** The server holds more workspaces than the list is allowed to load. */\n truncated: computed(() => workspaces().length < pagination().total && workspaces().length >= MAX_LOADED_WORKSPACES),\n selectedCount: computed(() => selectedIds().length),\n selectedIdSet: computed(() => new Set(selectedIds()))\n })),\n\n withMethods(store => {\n let loadingPromise: Promise<Workspace[]> | null = null;\n let loadingMorePromise: Promise<Workspace[]> | null = null;\n\n const setDocuments = (workspaceId: string, documents: WorkspaceDocument[]) => {\n patchState(store, state => ({ documents: { ...state.documents, [workspaceId]: documents } }));\n };\n\n // Counts are moved by a delta rather than reset to the cached list's length:\n // the cache holds at most the pages loaded so far, while `documentCount` is\n // the server's total.\n // The same goes for the documents' pagination total, which `hasMoreDocuments` compares\n // to the cached list: left as it was loaded, a workspace emptied of its documents would\n // still offer to load more.\n const adjustDocumentCount = (workspaceId: string, delta: number) => {\n patchState(store, state => {\n const pagination = state.documentPagination[workspaceId];\n return {\n workspaces: withDocumentCountAdjusted(state.workspaces, workspaceId, delta),\n documentPagination: pagination\n ? { ...state.documentPagination, [workspaceId]: { ...pagination, total: Math.max(0, pagination.total + delta) } }\n : state.documentPagination\n };\n });\n };\n\n const loadDocuments = async (workspaceId: string, signal?: AbortSignal): Promise<WorkspaceDocument[]> => {\n const res = await listWorkspaceDocuments(workspaceId, { page: 1, pageSize: PAGE_SIZE }, { signal });\n const documents = res.documents.map(mapWireDocumentToWorkspaceDocument);\n patchState(store, state => ({\n documentPagination: { ...state.documentPagination, [workspaceId]: { page: res.page, total: res.total } }\n }));\n setDocuments(workspaceId, documents);\n return documents;\n };\n\n // Deleting everything that was loaded does not mean the workspace is empty: the server may hold\n // pages that were never loaded. Show them rather than an empty state next to a \"load more\".\n // The deletion already succeeded, so a failed refill is not an error here: the cache stays empty\n // and \"load more\" (which then asks for the first page) lets the user retry.\n const refillIfEmptied = async (workspaceId: string) => {\n if ((store.documents()[workspaceId] ?? []).length || !(store.documentPagination()[workspaceId]?.total ?? 0)) return;\n try {\n await loadDocuments(workspaceId);\n } catch {\n // see above\n }\n };\n\n const load = async (): Promise<Workspace[]> => {\n patchState(store, { loading: true });\n try {\n const res = await listWorkspaces({ page: 1, pageSize: PAGE_SIZE });\n patchState(store, {\n workspaces: res.workspaces.map(mapWireWorkspaceToWorkspace),\n pagination: { page: res.page, total: res.total },\n loaded: true\n });\n return store.workspaces();\n } finally {\n patchState(store, { loading: false });\n loadingPromise = null;\n }\n };\n\n return {\n /** The cached workspace, or `undefined`. Reactive when read from a template or a `computed`. */\n workspace(id: string): Workspace | undefined {\n return store.workspaces().find(w => w.id === id);\n },\n\n /** The cached documents of a workspace (empty until {@link loadDocuments} ran). */\n documentsOf(workspaceId: string): WorkspaceDocument[] {\n return store.documents()[workspaceId] ?? [];\n },\n\n hasMoreDocuments(workspaceId: string): boolean {\n const pagination = store.documentPagination()[workspaceId];\n return !!pagination && (store.documents()[workspaceId]?.length ?? 0) < pagination.total;\n },\n\n /**\n * Idempotent: every entry point that needs the list calls it; only the\n * first caller triggers a real fetch, the rest await the same in-flight promise.\n */\n ensureLoaded(): Promise<Workspace[]> {\n if (store.loaded()) return Promise.resolve(store.workspaces());\n loadingPromise ??= load();\n return loadingPromise;\n },\n\n /**\n * Unlike {@link ensureLoaded}, always goes back to the server — so\n * workspaces created by others appear and document counts are current when\n * coming back from a workspace's detail. Shares an in-flight load. A\n * failure leaves the cache as it was.\n */\n reload(): Promise<Workspace[]> {\n loadingPromise ??= load();\n return loadingPromise;\n },\n\n /** Shares an in-flight call, so a double click does not fetch (and append) the same page twice. */\n loadMore(): Promise<Workspace[]> {\n if (!store.hasMore()) return Promise.resolve(store.workspaces());\n loadingMorePromise ??= (async () => {\n try {\n const current = store.workspaces();\n // Deleting workspaces shifts the server's pages: `page + 1` would skip as many as were\n // deleted since. Ask for the page holding the first one not loaded yet (it may overlap\n // the loaded tail, hence the dedup).\n const page = Math.min(store.pagination().page + 1, Math.floor(current.length / PAGE_SIZE) + 1);\n const res = await listWorkspaces({ page, pageSize: PAGE_SIZE });\n patchState(store, state => {\n const loaded = new Set(state.workspaces.map(w => w.id));\n const fresh = res.workspaces.map(mapWireWorkspaceToWorkspace).filter(w => !loaded.has(w.id));\n return { workspaces: [...state.workspaces, ...fresh], pagination: { page: res.page, total: res.total } };\n });\n return store.workspaces();\n } finally {\n loadingMorePromise = null;\n }\n })();\n return loadingMorePromise;\n },\n\n /**\n * Fallback for a deep link opened before the list was loaded, or for an id\n * that is not on the loaded page: fetches the workspace directly instead\n * of assuming it is cached.\n */\n async fetch(id: string): Promise<Workspace> {\n const workspace = mapWireWorkspaceToWorkspace(await getWorkspace(id));\n patchState(store, state => ({\n workspaces: state.workspaces.some(w => w.id === id) ? state.workspaces.map(w => (w.id === id ? workspace : w)) : [...state.workspaces, workspace]\n }));\n return workspace;\n },\n\n async create({ name, description = \"\", tags = [], owners = [], readers = [] }: CreateWorkspaceInput): Promise<Workspace> {\n const workspace = mapWireWorkspaceToWorkspace(await createWorkspace({ name, description, tags, owners, readers }));\n patchState(store, state => ({\n workspaces: [...state.workspaces, workspace],\n // The server's total grew by one too: left alone, a full list would offer to load a phantom workspace.\n pagination: { ...state.pagination, total: state.pagination.total + 1 },\n documents: { ...state.documents, [workspace.id]: [] }\n }));\n return workspace;\n },\n\n /**\n * `patch` is wire-shaped (the body of `PUT .../container/{id}`), not\n * Workspace-shaped: callers changing owners/readers always send the two\n * full arrays, since the API replaces them rather than patching deltas.\n *\n * @returns The updated workspace, or `null` when `id` is not in the cache.\n */\n async update(id: string, patch: UpdateWorkspaceInput): Promise<Workspace | null> {\n if (!store.workspaces().some(w => w.id === id)) return null;\n const updated = mapWireWorkspaceToWorkspace(await updateWorkspace(id, patch));\n patchState(store, state => ({ workspaces: state.workspaces.map(w => (w.id === id ? updated : w)) }));\n return updated;\n },\n\n /**\n * Always a bulk call, even for a single id — the API only exposes a bulk\n * DELETE. Only the ids the server confirmed deleted are dropped from the\n * cache; a partial failure leaves the rest in place for the call site to report.\n */\n async remove(ids: string[]): Promise<RemoveWorkspacesResult> {\n const result = await deleteWorkspaces(ids);\n const succeeded = new Set(result.succeeded);\n patchState(store, state => ({\n workspaces: state.workspaces.filter(w => !succeeded.has(w.id)),\n // Followed like the documents' total: an emptied list must not offer to load more.\n pagination: { ...state.pagination, total: Math.max(0, state.pagination.total - result.succeeded.length) },\n documents: Object.fromEntries(Object.entries(state.documents).filter(([id]) => !succeeded.has(id))),\n selectedIds: state.selectedIds.filter(id => !succeeded.has(id))\n }));\n return { succeeded: result.succeeded, failed: result.failed.map(f => ({ id: f.workspaceId, error: f.error })) };\n },\n\n /**\n * Local only — for when a read already proved a workspace is gone from the\n * server (a 404), rather than this session deleting it: the detail view\n * calls this once the user actually leaves a workspace its background\n * refresh found deleted, instead of {@link remove}, which would call\n * DELETE on something already deleted. Kept in the cache until then, so\n * the view can still show its name while it explains what happened.\n */\n forget(id: string): void {\n patchState(store, state => ({\n workspaces: state.workspaces.filter(w => w.id !== id),\n pagination: state.workspaces.some(w => w.id === id) ? { ...state.pagination, total: Math.max(0, state.pagination.total - 1) } : state.pagination,\n documents: Object.fromEntries(Object.entries(state.documents).filter(([wid]) => wid !== id)),\n documentPagination: Object.fromEntries(Object.entries(state.documentPagination).filter(([wid]) => wid !== id)),\n selectedIds: state.selectedIds.filter(selected => selected !== id)\n }));\n },\n\n loadDocuments,\n\n /** Without this, a workspace with more than one page of documents is silently truncated. */\n async loadMoreDocuments(workspaceId: string): Promise<WorkspaceDocument[]> {\n const current = store.documents()[workspaceId] ?? [];\n const pagination = store.documentPagination()[workspaceId];\n if (!pagination || current.length >= pagination.total) return current;\n // Deleting documents shifts the server's pages: `pagination.page + 1` would skip as many\n // documents as were deleted since. Ask for the page holding the first one not loaded yet\n // (it may overlap the loaded tail, hence the dedup).\n const page = Math.min(pagination.page + 1, Math.floor(current.length / PAGE_SIZE) + 1);\n const res = await listWorkspaceDocuments(workspaceId, { page, pageSize: PAGE_SIZE });\n const loaded = new Set(current.map(d => d.id));\n const fresh = res.documents.map(mapWireDocumentToWorkspaceDocument).filter(d => !loaded.has(d.id));\n const merged = [...current, ...fresh];\n patchState(store, state => ({\n documentPagination: { ...state.documentPagination, [workspaceId]: { page: res.page, total: res.total } }\n }));\n setDocuments(workspaceId, merged);\n return merged;\n },\n\n /**\n * Local only: adds documents to the cache without calling the API. The upload\n * flow does not use it (a file enters the list once the listing returns it).\n */\n addDocuments(workspaceId: string, documents: WorkspaceDocument[]): void {\n setDocuments(workspaceId, [...(store.documents()[workspaceId] ?? []), ...documents]);\n adjustDocumentCount(workspaceId, documents.length);\n },\n\n /**\n * Local only, the counterpart of {@link addDocuments}: forgets documents without\n * calling the API.\n * `removeDocuments` is the one that deletes on the server.\n */\n discardDocuments(workspaceId: string, documentIds: string[]): void {\n const before = store.documents()[workspaceId] ?? [];\n const after = before.filter(d => !documentIds.includes(d.id));\n setDocuments(workspaceId, after);\n adjustDocumentCount(workspaceId, after.length - before.length);\n },\n\n /** Local only: merges a patch into one cached document, matched by id. */\n patchDocument(workspaceId: string, documentId: string, patch: Partial<WorkspaceDocument>): void {\n patchState(store, state => ({\n documents: {\n ...state.documents,\n [workspaceId]: (state.documents[workspaceId] ?? []).map(d => (d.id === documentId ? { ...d, ...patch } : d))\n }\n }));\n },\n\n /**\n * \"Save to workspace\": attaches an already-existing document (no file\n * transfer) to a workspace. The backend contract for this is provisional.\n */\n async saveDocuments(workspaceId: string, documents: Pick<WorkspaceDocument, \"name\">[]): Promise<WorkspaceDocument[]> {\n const created = await addWorkspaceDocuments(\n workspaceId,\n documents.map(d => ({ name: d.name }))\n );\n const mapped = created.map(mapWireDocumentToWorkspaceDocument);\n setDocuments(workspaceId, [...(store.documents()[workspaceId] ?? []), ...mapped]);\n adjustDocumentCount(workspaceId, mapped.length);\n return mapped;\n },\n\n async removeDocuments(workspaceId: string, documentIds: string[]): Promise<void> {\n await deleteWorkspaceDocuments(workspaceId, documentIds);\n const before = store.documents()[workspaceId] ?? [];\n const after = before.filter(d => !documentIds.includes(d.id));\n setDocuments(workspaceId, after);\n adjustDocumentCount(workspaceId, after.length - before.length);\n await refillIfEmptied(workspaceId);\n },\n\n /**\n * \"Move to workspace\". There is no dedicated re-parent endpoint, so this\n * attaches a copy in the target first and only deletes the originals from\n * the source once that succeeded: a failed attach leaves the source\n * untouched instead of losing the documents.\n */\n async moveDocuments(fromWorkspaceId: string, toWorkspaceId: string, documentIds: string[]): Promise<void> {\n const from = store.documents()[fromWorkspaceId] ?? [];\n const moved = from.filter(d => documentIds.includes(d.id));\n if (!moved.length) return;\n\n const created = await addWorkspaceDocuments(\n toWorkspaceId,\n moved.map(d => ({ name: d.name }))\n );\n setDocuments(toWorkspaceId, [...(store.documents()[toWorkspaceId] ?? []), ...created.map(mapWireDocumentToWorkspaceDocument)]);\n adjustDocumentCount(toWorkspaceId, created.length);\n\n await deleteWorkspaceDocuments(fromWorkspaceId, documentIds);\n const remaining = from.filter(d => !documentIds.includes(d.id));\n setDocuments(fromWorkspaceId, remaining);\n adjustDocumentCount(fromWorkspaceId, remaining.length - from.length);\n await refillIfEmptied(fromWorkspaceId);\n },\n\n toggleSelected(id: string, checked: boolean): void {\n patchState(store, state => {\n const without = state.selectedIds.filter(selected => selected !== id);\n return { selectedIds: checked ? [...without, id] : without };\n });\n },\n\n clearSelection(): void {\n patchState(store, { selectedIds: [] });\n }\n };\n })\n);\n","import { computed } from \"@angular/core\";\nimport { withDevtools } from \"@angular-architects/ngrx-toolkit\";\nimport { patchState, signalStore, withComputed, withMethods, withState } from \"@ngrx/signals\";\nimport type { UploadJobStatus } from \"@sinequa/atomic\";\nimport type { ConnectionNotice, TrackedUpload, TrackFileInput, UploadStatus } from \"../models/upload.model\";\n\nexport interface TrackerWorkspace {\n id: string;\n name: string;\n}\n\nexport interface UploadTrackerState {\n uploads: TrackedUpload[];\n /** The workspaces being uploaded to, in the order they were first seen. */\n workspaces: TrackerWorkspace[];\n connection: ConnectionNotice | null;\n /** Files dropped from the tracker because their whole workspace finished — they still count as indexed. */\n clearedCount: number;\n /** What the server last said about each job's indexing, 0-100: indexed files / files of the job. */\n jobIndexing: Record<string, number>;\n /**\n * Jobs the user got rid of — closed, cancelled, cleared. The status API keeps listing a\n * job for a while after it ends, so without this a job closed by hand would come\n * straight back as an upload started elsewhere. Survives `reset()`, on purpose.\n */\n ignoredJobs: Record<string, true>;\n}\n\nconst initialState: UploadTrackerState = {\n uploads: [],\n workspaces: [],\n connection: null,\n clearedCount: 0,\n jobIndexing: {},\n ignoredJobs: {}\n};\n\n/** A job the status API reports that this client did not start — or no longer remembers starting. */\nexport interface RemoteJobEntry {\n job: UploadJobStatus;\n workspaceId: string;\n workspaceName: string;\n /** The current user started the job (`initiatedBy` is the current user's id). */\n mine: boolean;\n}\n\nconst FINISHED: readonly UploadStatus[] = [\"done\", \"error\", \"rejected\"];\nconst FAILED: readonly UploadStatus[] = [\"error\", \"rejected\"];\n\nexport const isFinished = (upload: TrackedUpload) => FINISHED.includes(upload.status);\nexport const isFailed = (upload: TrackedUpload) => FAILED.includes(upload.status);\n\n/** The job an upload belongs to: ids are `<jobId>/<fileName>`. */\nconst jobOf = (upload: TrackedUpload) => upload.id.slice(0, upload.id.indexOf(\"/\"));\n\nexport interface PhaseProgress {\n uploading: number;\n indexing: number;\n}\n\n/**\n * How far along a set of uploads is, for each of the two phases, as one 0-100 number.\n *\n * Measured over the files of every job that still has something moving in the phase —\n * and over ALL of that job's files (a failed or refused one aside), not just the ones\n * currently listed in the phase. A file that moved on is a file that finished the phase:\n * counting only the ones left would make the bar drop, or stall, each time one finishes.\n * That is also how the server counts (`indexed files / files of the job`), so the two agree.\n *\n * - uploading: the files still sending count their own percentage, the others are at 100;\n * - indexing: the API has no per-file percentage, only the job's aggregate. What the\n * server last said and what is already known locally (files done / files) are both\n * lower bounds of the truth: the larger one wins, so the bar never falls back.\n */\nexport function phaseProgress(uploads: readonly TrackedUpload[], jobIndexing: Record<string, number>): PhaseProgress {\n const jobs = new Map<string, TrackedUpload[]>();\n for (const upload of uploads) {\n if (isFailed(upload)) continue;\n const files = jobs.get(jobOf(upload));\n if (files) files.push(upload);\n else jobs.set(jobOf(upload), [upload]);\n }\n\n let uploadingSum = 0;\n let uploadingFiles = 0;\n let indexingSum = 0;\n let indexingFiles = 0;\n for (const [jobId, files] of jobs) {\n if (files.some(f => f.status === \"uploading\")) {\n uploadingSum += files.reduce((sum, f) => sum + (f.status === \"uploading\" ? f.percent : 100), 0);\n uploadingFiles += files.length;\n }\n if (files.some(f => f.status === \"indexing\")) {\n const known = (files.filter(f => f.status === \"done\").length / files.length) * 100;\n indexingSum += Math.max(jobIndexing[jobId] ?? 0, known) * files.length;\n indexingFiles += files.length;\n }\n }\n return {\n uploading: uploadingFiles ? Math.round(uploadingSum / uploadingFiles) : 0,\n indexing: indexingFiles ? Math.round(indexingSum / indexingFiles) : 0\n };\n}\n\nfunction updateOne(uploads: TrackedUpload[], id: string, change: (upload: TrackedUpload) => TrackedUpload): TrackedUpload[] {\n return uploads.map(upload => (upload.id === id ? change(upload) : upload));\n}\n\n/** `ignored` plus the jobs of `uploads`. */\nfunction withIgnored(ignored: Record<string, true>, uploads: readonly TrackedUpload[]): Record<string, true> {\n const next = { ...ignored };\n for (const upload of uploads) next[jobOf(upload)] = true;\n return next;\n}\n\n/** The row standing for a whole job, whose files the API does not list: `<jobId>/`. */\nconst remoteJobRowId = (jobId: string) => `${jobId}/`;\n\n/** Where a whole job stands, as one row: what the API says of its two phases. */\nfunction remoteJobState(job: UploadJobStatus): Pick<TrackedUpload, \"status\" | \"percent\"> {\n if (job.uploadStatus === \"uploading\") return { status: \"uploading\", percent: job.uploadProgress?.percentage ?? 0 };\n if (job.uploadStatus === \"upload_interrupted\") return { status: \"error\", percent: 0 };\n if (job.indexingStatus === \"complete\") return { status: \"done\", percent: 100 };\n if (job.indexingStatus === \"failed\" || job.indexingStatus === \"interrupted\") return { status: \"error\", percent: 0 };\n // The job's aggregate indexing percentage lives in `jobIndexing`: no file has its own.\n return { status: \"indexing\", percent: 0 };\n}\n\n/**\n * The rows a job started elsewhere shows as: one per file the API names (errors and\n * rejections, the only per-file data it has), and one for the job itself — unless a\n * named file already explains why it failed.\n */\nfunction remoteRows({ job, workspaceId, mine }: RemoteJobEntry): TrackedUpload[] {\n const base = { type: \"doc\", workspaceId, retryable: false, percent: 0, remote: { mine } } as const;\n const files: TrackedUpload[] = [\n ...(job.errors ?? []).map(e => ({ ...base, id: `${job.jobId}/${e.fileName}`, name: e.fileName, status: \"error\" as const, message: e.error ?? undefined })),\n ...(job.rejected ?? []).map(r => ({ ...base, id: `${job.jobId}/${r.fileName}`, name: r.fileName, status: \"rejected\" as const, message: r.reason }))\n ];\n const state = remoteJobState(job);\n if (state.status === \"error\" && files.length) return files;\n return [{ ...base, ...state, id: remoteJobRowId(job.jobId), name: \"\" }, ...files];\n}\n\nconst sameRow = (a: TrackedUpload, b: TrackedUpload) =>\n a.status === b.status && a.percent === b.percent && a.message === b.message && a.workspaceId === b.workspaceId && a.remote?.mine === b.remote?.mine;\n\n/**\n * The state of every upload the tracker follows — pure state, no I/O.\n *\n * `UploadService` writes to it as bytes move and as the server reports; the\n * tracker panel only reads it. Because nothing here talks to the network, a\n * test or a demo page can drive the panel by injecting this store and calling\n * `track()` / `setProgress()` / `setDone()`… directly.\n *\n * Root-scoped: an upload must outlive the page that started it.\n */\nexport const UploadTrackerStore = signalStore(\n { providedIn: \"root\" },\n withDevtools(\"UploadTracker\"),\n\n withState(initialState),\n\n withComputed(({ uploads, clearedCount, jobIndexing }) => ({\n /** Whether there is anything to show — the panel is hidden otherwise. */\n active: computed(() => uploads().length > 0),\n failed: computed(() => uploads().filter(isFailed)),\n byStatus: computed(() => {\n const groups: Record<UploadStatus, TrackedUpload[]> = { uploading: [], indexing: [], done: [], error: [], rejected: [] };\n for (const upload of uploads()) groups[upload.status].push(upload);\n return groups;\n }),\n /** Everything is over and nothing failed: the panel celebrates, then dismisses itself. */\n allDone: computed(() => uploads().length > 0 && uploads().every(u => u.status === \"done\")),\n /** Everything is over, whether it worked or not: nothing moves any more, and the panel may be closed. */\n settled: computed(() => uploads().length > 0 && uploads().every(isFinished)),\n /**\n * A failure on an upload of the current user's. It is theirs to read and close: the panel\n * stays until they do. A failure on someone else's upload is only worth the usual delay.\n */\n authorFailed: computed(() => uploads().some(u => isFailed(u) && (!u.remote || u.remote.mine))),\n /** Per workspace, how far each phase is — what the tracker's progress bars show. */\n progress: computed(() => {\n const byWorkspace = new Map<string, TrackedUpload[]>();\n for (const upload of uploads()) {\n const own = byWorkspace.get(upload.workspaceId);\n if (own) own.push(upload);\n else byWorkspace.set(upload.workspaceId, [upload]);\n }\n const progress: Record<string, PhaseProgress> = {};\n for (const [workspaceId, own] of byWorkspace) progress[workspaceId] = phaseProgress(own, jobIndexing());\n return progress;\n }),\n /** Every file ever tracked that got indexed — the ones already cleared plus the ones still listed. */\n indexedCount: computed(() => clearedCount() + uploads().filter(u => u.status === \"done\").length)\n })),\n\n withMethods(store => ({\n /** Starts following files that are being sent to a workspace. Re-tracking a known id restarts it. */\n track(files: TrackFileInput[], workspaceId: string, workspaceName: string): void {\n patchState(store, state => {\n const ids = new Set(files.map(f => f.id));\n const added: TrackedUpload[] = files.map(f => ({\n id: f.id,\n name: f.name,\n type: f.type,\n workspaceId,\n status: \"uploading\",\n percent: 0,\n retryable: false\n }));\n const known = state.workspaces.some(w => w.id === workspaceId);\n return {\n uploads: [...state.uploads.filter(u => !ids.has(u.id)), ...added],\n workspaces: known\n ? state.workspaces.map(w => (w.id === workspaceId ? { ...w, name: workspaceName } : w))\n : [...state.workspaces, { id: workspaceId, name: workspaceName }]\n };\n });\n },\n\n setProgress(id: string, percent: number): void {\n patchState(store, state => ({ uploads: updateOne(state.uploads, id, u => ({ ...u, percent })) }));\n },\n\n /** The bytes are in; indexing is now the server's business. */\n setIndexing(id: string): void {\n patchState(store, state => ({\n uploads: updateOne(state.uploads, id, u => ({ ...u, status: \"indexing\", percent: 0, message: undefined, retryable: false }))\n }));\n },\n\n setDone(id: string): void {\n patchState(store, state => ({\n uploads: updateOne(state.uploads, id, u => ({ ...u, status: \"done\", percent: 100, message: undefined, retryable: false }))\n }));\n },\n\n setError(id: string, options: { message?: string; retryable?: boolean } = {}): void {\n patchState(store, state => ({\n uploads: updateOne(state.uploads, id, u => ({ ...u, status: \"error\", percent: 0, message: options.message, retryable: options.retryable ?? false }))\n }));\n },\n\n /** A failed transfer is being sent again. */\n restart(id: string): void {\n patchState(store, state => ({\n uploads: updateOne(state.uploads, id, u => ({ ...u, status: \"uploading\", percent: 0, message: undefined, retryable: false }))\n }));\n },\n\n /**\n * Feeds in the job-level half of the status API: the per-file error\n * messages and rejections only `GET .../agent-workspace/status` reports,\n * and the job's aggregate indexing percentage (kept per job: no file has its own).\n *\n * Ids are `<jobId>/<fileName>` — the job and name the API reports errors\n * and rejections under — so they map back with a plain lookup.\n *\n * @returns Whether anything changed, which paces the poller.\n */\n applyJobs(jobs: UploadJobStatus[]): boolean {\n let next = store.uploads();\n let jobIndexing = store.jobIndexing();\n let changed = false;\n const change = (id: string, edit: (u: TrackedUpload) => TrackedUpload | null) => {\n next = next.map(u => {\n if (u.id !== id) return u;\n const edited = edit(u);\n if (!edited) return u;\n changed = true;\n return edited;\n });\n };\n\n for (const job of jobs) {\n for (const e of job.errors ?? []) {\n const message = e.error ?? undefined;\n change(`${job.jobId}/${e.fileName}`, u =>\n u.status !== \"error\" || u.message !== message ? { ...u, status: \"error\", percent: 0, message, retryable: false } : null\n );\n }\n for (const r of job.rejected ?? []) {\n change(`${job.jobId}/${r.fileName}`, u =>\n u.status !== \"rejected\" || u.message !== r.reason ? { ...u, status: \"rejected\", percent: 0, message: r.reason, retryable: false } : null\n );\n }\n // The job is over: whatever is still indexing in it (and was neither refused nor\n // failed, handled above) made it. This does not depend on the document listing,\n // whose ids differ from ours and which is only read for the open workspace.\n const complete = job.indexingStatus === \"complete\";\n if (complete) {\n next = next.map(u => {\n if (u.status !== \"indexing\" || !u.id.startsWith(`${job.jobId}/`)) return u;\n changed = true;\n return { ...u, status: \"done\", percent: 100, message: undefined, retryable: false };\n });\n }\n\n // The aggregate is the finest grain the API has: it moves one file at a time (indexed / total).\n // A finished job reports 0 on some servers, so completeness is read from the status, not the number.\n // Only jobs we follow are kept: the status endpoint also lists every older job of the workspace.\n const percent = complete ? 100 : job.indexingStatus === \"indexing\" ? job.indexingProgress?.percentage : undefined;\n if (typeof percent === \"number\" && jobIndexing[job.jobId] !== percent && next.some(u => u.id.startsWith(`${job.jobId}/`))) {\n jobIndexing = { ...jobIndexing, [job.jobId]: percent };\n changed = true;\n }\n }\n\n if (changed) patchState(store, { uploads: next, jobIndexing });\n return changed;\n },\n\n /**\n * Feeds in the jobs the status API reports that this client is not following — the ones\n * another user started, or its own from before a page reload. Each becomes a row (see\n * `remoteRows`), updated on every call; a job this client sent itself is left to the\n * local rows, and so is one the user already got rid of.\n *\n * @returns Whether anything changed, which paces the poller.\n */\n applyRemoteJobs(entries: RemoteJobEntry[]): boolean {\n const current = store.uploads();\n const ignored = store.ignoredJobs();\n const ownJobs = new Set(current.filter(u => !u.remote).map(jobOf));\n const rows = new Map(current.map(u => [u.id, u]));\n const workspaces = [...store.workspaces()];\n let changed = false;\n\n for (const entry of entries) {\n if (ignored[entry.job.jobId] || ownJobs.has(entry.job.jobId)) continue;\n for (const row of remoteRows(entry)) {\n const known = rows.get(row.id);\n if (known && sameRow(known, row)) continue;\n rows.set(row.id, known ? { ...known, ...row } : row);\n changed = true;\n }\n if (!workspaces.some(w => w.id === entry.workspaceId)) {\n workspaces.push({ id: entry.workspaceId, name: entry.workspaceName });\n changed = true;\n }\n }\n\n if (changed) patchState(store, { uploads: [...rows.values()], workspaces });\n return changed;\n },\n\n setConnection(connection: ConnectionNotice | null): void {\n patchState(store, { connection });\n },\n\n remove(id: string): void {\n patchState(store, state => {\n const uploads = state.uploads.filter(u => u.id !== id);\n // The job is forgotten once its last row is gone; while one is left, the job is still followed.\n const gone = state.uploads.filter(u => u.id === id && !uploads.some(other => jobOf(other) === jobOf(u)));\n return { uploads, ignoredJobs: withIgnored(state.ignoredJobs, gone) };\n });\n },\n\n /** Drops a finished workspace's files from the tracker; they stay counted as indexed. */\n removeWorkspace(workspaceId: string): void {\n patchState(store, state => ({\n uploads: state.uploads.filter(u => u.workspaceId !== workspaceId),\n workspaces: state.workspaces.filter(w => w.id !== workspaceId),\n clearedCount: state.clearedCount + state.uploads.filter(u => u.workspaceId === workspaceId).length,\n ignoredJobs: withIgnored(\n state.ignoredJobs,\n state.uploads.filter(u => u.workspaceId === workspaceId)\n )\n }));\n },\n\n reset(): void {\n patchState(store, state => ({ ...initialState, ignoredJobs: withIgnored(state.ignoredJobs, state.uploads) }));\n }\n }))\n);\n","import { Injectable, signal } from \"@angular/core\";\nimport { fetchPrincipal } from \"@sinequa/atomic\";\n\nexport interface CurrentUser {\n /** The principal's `userId` (`domain|id`) — the id space a workspace's owners/readers use. */\n id: string;\n name: string;\n}\n\n/**\n * The logged-in user's identity — the one thing @sinequa/atomic has no\n * synchronous notion of. Resolved once, on first injection, through\n * `fetchPrincipal()`; `user()` is `undefined` until it settles (and stays so\n * if the request fails).\n *\n * Callers render off `user()` straight away and treat \"unknown\" as a valid\n * fallback (a raw-id badge, the generic \"You\" label) rather than an error.\n * Those that must not act on \"unknown\" as if it were an answer — the\n * \"you can't remove yourself\" rule needs to know which row is yours —\n * await {@link whenReady}.\n */\n@Injectable({ providedIn: \"root\" })\nexport class CurrentUserService {\n private readonly _user = signal<CurrentUser | undefined>(undefined);\n private readonly ready: Promise<CurrentUser | undefined>;\n\n readonly user = this._user.asReadonly();\n\n constructor() {\n this.ready = Promise.resolve()\n .then(() => fetchPrincipal())\n // `userId`, not `id`: the container API rejects an identity that is not in\n // `domain|id` form, and Principal.id is the bare account id. No fallback to\n // `id` if `userId` is somehow empty: `id` is precisely the form the API\n // refuses, so falling back would guarantee the 400 instead of avoiding it.\n // Leaving the user unknown is the softer failure.\n .then(p => {\n const user = p?.userId ? { id: p.userId, name: p.fullName || p.name } : undefined;\n this._user.set(user);\n return user;\n })\n .catch(() => undefined);\n }\n\n /** The same identity as `user()`, but awaitable. Resolves `undefined` if the principal request failed. */\n whenReady(): Promise<CurrentUser | undefined> {\n return this.ready;\n }\n}\n","import { globalConfig } from \"@sinequa/atomic\";\n\n/**\n * The configured backend as an absolute base URL, or `undefined` when no\n * `backendUrl` is configured (same-origin host — atomic itself keeps its\n * endpoints relative in that case).\n */\nfunction backendBase(): string | undefined {\n const backendUrl = globalConfig.backendUrl;\n if (!backendUrl) return undefined;\n // Trailing slash so a relative endpoint resolves *under* backendUrl instead\n // of replacing its last path segment; any slash the host already wrote is\n // collapsed first. `backendUrl` may itself be relative ('/sinequa').\n return new URL(`${backendUrl.replace(/\\/+$/, \"\")}/`, window.location.href).href;\n}\n\n/**\n * Resolves a path that atomic does not fetch for us to an absolute URL: the\n * tus upload endpoint, and the `documentCachedContentUrl` the preview web\n * service returns for an `<iframe src>`. Both are URLs the browser loads\n * directly rather than through atomic's own `get()`/`post()`, so neither picks\n * up `backendUrl` on its own.\n *\n * - an absolute endpoint always wins (it is taken as-is);\n * - with a `backendUrl` configured, a root-relative endpoint\n * ('/api/v2/agent-workspace/ingest/file') resolves *under* it, so a backendUrl\n * carrying a path prefix ('https://host/sinequa') isn't bypassed;\n * - with no `backendUrl`, it resolves against the page.\n */\nexport function resolveEndpointUrl(endpoint: string): URL {\n const base = backendBase();\n if (!base) return new URL(endpoint, window.location.href);\n return new URL(endpoint.replace(/^\\/+/, \"\"), base);\n}\n","// Helpers over @sinequa/atomic's fetchUploadStatus() response, which groups\n// jobs by workspace.\nimport type { UploadJobStatus, UploadStatusResponse } from \"@sinequa/atomic\";\n\n/** True once a job can no longer change — nothing left to poll for. */\nexport function isJobTerminal(job: UploadJobStatus): boolean {\n if (job.uploadStatus === \"upload_interrupted\") return true;\n return job.indexingStatus === \"complete\" || job.indexingStatus === \"failed\" || job.indexingStatus === \"interrupted\";\n}\n\n/** Flattens the grouped response into a plain job list. */\nexport function allJobs(res: UploadStatusResponse): UploadJobStatus[] {\n return (res.workspaces ?? []).flatMap(c => c.jobs ?? []);\n}\n\n/** A job and the workspace the response grouped it under. */\nexport interface WorkspaceJob {\n job: UploadJobStatus;\n workspaceId: string;\n workspaceName: string;\n}\n\n/** Like {@link allJobs}, but keeps the workspace each job belongs to. */\nexport function workspaceJobs(res: UploadStatusResponse): WorkspaceJob[] {\n return (res.workspaces ?? []).flatMap(c => (c.jobs ?? []).map(job => ({ job, workspaceId: c.workspaceId, workspaceName: c.workspaceName })));\n}\n\n/** How long the panel stays on screen once every upload is over. */\nexport const FINISHED_UPLOAD_VISIBLE_MS = 2 * 60 * 1000;\n","/**\n * `crypto.randomUUID()` is only available in a secure context (HTTPS, or HTTP on\n * localhost). This falls back to a `getRandomValues()`-based UUID v4 so the\n * upload still works over plain HTTP on a hostname or an IP.\n */\nexport function generateUUID(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx\n\n const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","// Silent, non-navigating attempt to make the session usable again after a 401.\n//\n// Why this exists: an upload that outlives a connection drop comes back to a\n// server that has since forgotten it. tus keeps retrying (see\n// UPLOAD_RETRY_DELAYS in tus-upload.ts) but every attempt carries the\n// stale CSRF token it was given at creation time, so every attempt is a 401 —\n// the retry budget burns down without a single byte moving. The status poll\n// fails the same way at the same moment, for the same reason.\n//\n// Recovering means getting a fresh token into storage; atomic's createHeaders()\n// reads it per request, so tus's onBeforeRequest picks it up on the very next\n// attempt with nothing else to do.\nimport { tryAutoAuthentication } from \"@sinequa/atomic\";\n\nlet _inFlight: Promise<boolean> | null = null;\n\n/**\n * Re-establishes the session without leaving the page, and reports whether it\n * worked.\n *\n * Deliberately `tryAutoAuthentication()` and nothing else:\n *\n * - `hasSession()` is the wrong probe. It answers `true` immediately when a\n * token is in storage, without asking the server — and a stale token in\n * storage is precisely the situation being recovered from, so it would report\n * success while every request kept coming back 401.\n * - `login()` is the wrong door. In `oauth`/`saml` mode it navigates to the\n * identity provider, and a page load destroys every in-flight upload: tus\n * holds the `File` in memory and nothing persists it. Signing in again has to\n * stay the user's explicit choice, not something a background retry triggers.\n *\n * `tryAutoAuthentication()` hits a protected endpoint with `redirect: \"manual\"`,\n * so it never navigates, and a `200` carries the `sinequa-jwt-refresh` header\n * that atomic stores for us. It covers the cases worth covering automatically:\n * an ambient SSO session (IIS/proxy), an OIDC session still live at the IdP, and\n * a cookie that outlived its CSRF token. It answers `false` — never throws —\n * when the user is genuinely signed out, which is the caller's cue to stop\n * retrying and say so.\n *\n * Single-flight: several uploads and the status poll all hit 401 within the same\n * instant, and they share one probe rather than racing a dozen of them.\n *\n * @returns `true` when the session is usable again.\n */\nexport function recoverSession(): Promise<boolean> {\n _inFlight ??= tryAutoAuthentication()\n .catch(() => false)\n .finally(() => {\n _inFlight = null;\n });\n return _inFlight;\n}\n","// One cancellable polling loop, shared by every upload.\n//\n// The loop paces itself from what a tick reports:\n// - `pending: false` -> nothing left to watch, stop (start() revives it);\n// - `changed: true` -> something moved, keep the base interval;\n// - `changed: false` -> idle, widen the delay geometrically up to maxIntervalMs\n// so a job the server never resolves costs a request a minute, not one a second.\n// A thrown tick backs off the same way and does NOT stop the loop: a network\n// blip should not silently end the tracking.\n\nexport interface PollResult {\n /** Something is still being watched — keep polling. */\n pending: boolean;\n /** This tick observed a change; resets the idle backoff. */\n changed?: boolean;\n}\n\nexport interface StatusPollerOptions {\n /** Delay between ticks while things are moving (default 1500 ms). */\n intervalMs?: number;\n /** Ceiling the idle/failure backoff climbs to (default 30000 ms). */\n maxIntervalMs?: number;\n /** Runs one poll. `signal` aborts when the poller is stopped mid-flight. */\n tick(signal: AbortSignal): Promise<PollResult>;\n}\n\nexport class StatusPoller {\n private readonly _intervalMs: number;\n private readonly _maxIntervalMs: number;\n private readonly _tick: (signal: AbortSignal) => Promise<PollResult>;\n\n private _timer: ReturnType<typeof setTimeout> | null = null;\n private _abort: AbortController | null = null;\n private _running = false;\n private _idleSteps = 0;\n\n constructor(options: StatusPollerOptions) {\n this._intervalMs = options.intervalMs ?? 1500;\n this._maxIntervalMs = options.maxIntervalMs ?? 30000;\n this._tick = options.tick;\n }\n\n get running(): boolean {\n return this._running;\n }\n\n /** Idempotent: calling it while already polling does nothing. */\n start(): void {\n if (this._running) return;\n this._running = true;\n this._idleSteps = 0;\n this._schedule(0);\n }\n\n /** Idempotent, and leaves the poller restartable. */\n stop(): void {\n this._running = false;\n if (this._timer) {\n clearTimeout(this._timer);\n this._timer = null;\n }\n this._abort?.abort();\n this._abort = null;\n }\n\n private _schedule(delay: number): void {\n if (!this._running) return;\n this._timer = setTimeout(() => {\n this._timer = null;\n void this._run();\n }, delay);\n }\n\n private _backoffDelay(): number {\n return Math.min(this._intervalMs * 2 ** this._idleSteps, this._maxIntervalMs);\n }\n\n private async _run(): Promise<void> {\n if (!this._running) return;\n const controller = new AbortController();\n this._abort = controller;\n try {\n const result = await this._tick(controller.signal);\n if (!this._running) return;\n if (!result.pending) {\n this.stop();\n return;\n }\n if (result.changed) this._idleSteps = 0;\n else this._idleSteps++;\n this._schedule(result.changed ? this._intervalMs : this._backoffDelay());\n } catch {\n // An abort is a deliberate stop, not a failure — nothing to re-arm.\n if (controller.signal.aborted || !this._running) return;\n this._idleSteps++;\n this._schedule(this._backoffDelay());\n } finally {\n if (this._abort === controller) this._abort = null;\n }\n }\n}\n","import { InjectionToken } from \"@angular/core\";\nimport { createHeaders } from \"@sinequa/atomic\";\nimport { DetailedError, Upload } from \"tus-js-client\";\n\n/**\n * Retry schedule for a tus chunk, in ms. tus caps the number of attempts at this\n * array's LENGTH, so surviving an outage is a matter of how many entries there\n * are, not just how long they are. The total (~5 min) is deliberately sized on\n * the server's own retention: its cleanup reclaims an incomplete upload idle for\n * 5 minutes and then answers 410 Gone, so retrying past that cannot succeed — at\n * which point the upload is recovered by the `online` handler instead.\n */\nexport const UPLOAD_RETRY_DELAYS = [0, 1000, 3000, 5000, 10000, 15000, 20000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000];\n\n/**\n * Statuses the ingest endpoint uses to say \"this upload will never be accepted\":\n * retrying them only burns requests. Everything else — a network error (status\n * 0), a 5xx, a 429 — is transient and worth another attempt.\n *\n * 401 is deliberately NOT in here even though it is a 4xx. An upload that\n * outlives a connection drop comes back holding a CSRF token the server has\n * since forgotten, so every attempt 401s and the whole retry budget burns\n * without a byte moving — which is exactly the failure this list would make\n * permanent. It is handled on its own by the service: recover the session, then\n * let the same retry carry the fresh token.\n */\nexport const FATAL_UPLOAD_STATUSES: ReadonlySet<number> = new Set([400, 403, 404, 409, 410, 413, 422, 507]);\n\nconst CHUNK_SIZE = 5 * 1024 * 1024;\n\n/** What an upload tells whoever drives it. */\nexport interface UploadEvents {\n /**\n * Whether tus should try again after a failed attempt. `status` is the HTTP\n * status of the failure, 0 for a network error; `attempt` is 0-based and is\n * the index into {@link UPLOAD_RETRY_DELAYS} of the delay tus waited before it.\n */\n shouldRetry(status: number, attempt: number): boolean;\n /** Any response came back — `status` tells whether the connection is working again. */\n responded(status: number): void;\n /** Resolves once the request may go out: a session recovery in flight has to finish first, it is what the retry is for. */\n beforeRequest(): Promise<void>;\n progress(bytesUploaded: number, bytesTotal: number | null): void;\n /** The server holds every byte. Indexing is a separate server-side job with no tus equivalent. */\n success(): void;\n /** The retry budget is spent, or the server refused for good. */\n failed(error: Error): void;\n}\n\nexport interface UploadOptions {\n /** Absolute: tus must not resolve a relative endpoint against the host page instead of the configured backend. */\n endpoint: string;\n /** The ingest endpoint validates these keys and answers 400 if one is missing. */\n metadata: Record<string, string>;\n events: UploadEvents;\n}\n\n/** An upload in flight, whatever moves its bytes. */\nexport interface UploadHandle {\n /** Starts it — resuming a previous attempt at the same file when tus recognises it (same name, size, type, date). */\n begin(): void;\n /** Sends it again after a failure; tus re-reads the server's offset first, so only the missing bytes go out. */\n resume(): void;\n /**\n * Stops it. With `terminate`, the server is asked to drop what it received\n * (tus termination extension) — which it may not support, in which case this rejects.\n */\n abort(terminate: boolean): Promise<void>;\n}\n\nexport type UploadFactory = (file: File, options: UploadOptions) => UploadHandle;\n\n/** The HTTP status behind a tus error: 0 when there was no response (a network error). */\nexport function tusErrorStatus(error: DetailedError): number {\n return error.originalResponse ? error.originalResponse.getStatus() : 0;\n}\n\n/** Sends `file` with tus. */\nexport const createTusUpload: UploadFactory = (file, { endpoint, metadata, events }) => {\n const upload = new Upload(file, {\n endpoint,\n chunkSize: CHUNK_SIZE,\n uploadSize: file.size,\n retryDelays: UPLOAD_RETRY_DELAYS,\n removeFingerprintOnSuccess: true,\n metadata,\n // tus's default policy stops retrying the moment `navigator.onLine` reports\n // false — exactly when a dropped connection needs riding out, which is why a\n // short outage used to kill the transfer outright. Retry on anything\n // transient and let the schedule (or the `online` handler) do the waiting.\n onShouldRetry: (error, attempt) => events.shouldRetry(tusErrorStatus(error), attempt),\n onAfterResponse: (_request, response) => events.responded(response.getStatus()),\n // tus awaits this hook, so waiting here is what makes the first retry (delay\n // 0) useful instead of a guaranteed second 401. The headers are read per\n // attempt, so a refreshed token is picked up with nothing else to do.\n onBeforeRequest: async request => {\n await events.beforeRequest();\n createHeaders().forEach((value, key) => request.setHeader(key, value));\n },\n onProgress: (bytesUploaded, bytesTotal) => events.progress(bytesUploaded, bytesTotal),\n onSuccess: () => events.success(),\n onError: error => events.failed(error)\n });\n\n return {\n begin: () => {\n upload\n .findPreviousUploads()\n .catch(() => [])\n .then(previous => {\n if (previous.length) upload.resumeFromPreviousUpload(previous[0]);\n upload.start();\n });\n },\n resume: () => upload.start(),\n abort: terminate => upload.abort(terminate)\n };\n};\n\n/** Overridable so the service can be driven without a network. */\nexport const UPLOAD_FACTORY = new InjectionToken<UploadFactory>(\"UPLOAD_FACTORY\", {\n providedIn: \"root\",\n factory: () => createTusUpload\n});\n","import { DestroyRef, inject, Injectable } from \"@angular/core\";\nimport { ApiError, fetchUploadStatus, onAuthEvent } from \"@sinequa/atomic\";\nimport type { UploadStatusResponse } from \"@sinequa/atomic\";\nimport type { ConnectionNotice } from \"../models/upload.model\";\nimport { CurrentUserService } from \"../services/current-user.service\";\nimport { WorkspacesStore } from \"../store/workspaces.store\";\nimport { resolveEndpointUrl } from \"../utils/base-url\";\nimport { getDocumentType } from \"../utils/doc-type\";\nimport { allJobs, workspaceJobs, isJobTerminal } from \"../utils/upload-jobs\";\nimport { generateUUID } from \"../utils/uuid\";\nimport { recoverSession } from \"./session-recovery\";\nimport { PollResult, StatusPoller } from \"./status-poller\";\nimport { FATAL_UPLOAD_STATUSES, UPLOAD_FACTORY, UPLOAD_RETRY_DELAYS, UploadHandle } from \"./tus-upload\";\nimport { RemoteJobEntry, UploadTrackerStore } from \"./upload-tracker.store\";\n\n/** A retry tus has scheduled: which attempt it is (0-based), and when it fires. */\ninterface RetrySchedule {\n attempt: number;\n retryAtMs: number;\n}\n\n/** A file being sent, as the service and the tracker follow it. `id` is `<jobId>/<fileName>`, which never changes. */\ninterface UploadItem {\n id: string;\n workspaceId: string;\n}\n\n/** An upload the service can still act on. */\ninterface UploadEntry {\n handle: UploadHandle;\n workspaceId: string;\n /** The server refused this file for good: sending it again would only be refused again. */\n refused: boolean;\n}\n\nexport interface UploadTarget {\n id: string;\n name: string;\n}\n\nexport interface AddFilesOptions {\n /** The tus endpoint — the `upload-endpoint` of the host. Relative paths resolve against the configured backend. */\n endpoint: string;\n}\n\n/**\n * Sends files to a workspace and follows them until they are indexed.\n *\n * Two independent things happen after a file is handed over, and both can stall\n * on the same cause (a lost connection, a lapsed session):\n *\n * - the byte transfer, by tus, which retries on its own schedule;\n * - indexing, a server-side job tus knows nothing about, observed by polling\n * the Document Upload status API.\n *\n * One small state machine here owns both, and the tracker's connection banner\n * speaks for both. What the user sees lives in `UploadTrackerStore`; this\n * service only writes to it.\n *\n * It never writes a file into a workspace's list: the list is the index, and a file\n * enters it only once the container listing returns it. What the service does for the\n * list is re-read that listing while something is in progress.\n *\n * Root-scoped: an upload must outlive the view that started it. It registers its\n * window and auth listeners once, and removes them when the application is destroyed.\n */\n@Injectable({ providedIn: \"root\" })\nexport class UploadService {\n private readonly tracker = inject(UploadTrackerStore);\n private readonly workspaces = inject(WorkspacesStore);\n private readonly currentUser = inject(CurrentUserService);\n private readonly createUpload = inject(UPLOAD_FACTORY);\n\n private readonly uploads = new Map<string, UploadEntry>();\n /**\n * Documents this client is still transferring or waiting on. The server's\n * status store can lag a job by a tick or two, so this is what keeps the\n * polling loop alive until it catches up — and emptying it lets it stop.\n */\n private readonly inFlight = new Set<string>();\n private readonly poller = new StatusPoller({ tick: signal => this.pollUploadStatus(signal) });\n private focusedWorkspaceId: string | null = null;\n\n // ── connection loss & session recovery ──\n /** Document id -> the retry tus has scheduled for it. Empty means nothing is waiting. */\n private readonly retrying = new Map<string, RetrySchedule>();\n /** A recovery is in flight. Distinct from `signedOut`: one is \"asking\", the other is the answer. */\n private recovering = false;\n /** The last recovery said the user is genuinely signed out. Reset the moment anything re-authenticates. */\n private signedOut = false;\n /** When the trouble started, so the banner can say for how long. 0 while healthy. */\n private lostSinceMs = 0;\n private recovery: Promise<boolean> | null = null;\n\n constructor() {\n const resume = () => this.resumeStalled();\n window.addEventListener(\"online\", resume);\n\n // The other way back in: the user signed in again (here, or in another tab\n // sharing this session storage). `login()` is never called from here — it\n // navigates for oauth/saml, and a page load destroys every in-flight upload\n // — so this is how a real sign-in reaches us.\n const offAuth = onAuthEvent(\"authenticated\", ({ authenticated }) => {\n if (!authenticated) return;\n this.signedOut = false;\n this.resumeStalled();\n this.poller.start();\n this.publishConnection();\n });\n\n inject(DestroyRef).onDestroy(() => {\n window.removeEventListener(\"online\", resume);\n offAuth?.();\n this.poller.stop();\n });\n }\n\n // ── public API ──\n\n /**\n * Stages the files as one ingest job and starts sending them. Everything the\n * user picked goes out as ONE batch (one job id).\n *\n * No client-side gatekeeping: no type allow-list, no count or size cap. What\n * can be indexed is the server's call (the workspace's conversion plan); the\n * files it will not take come back in the job's `rejected[]`. The server does\n * cap a job at 1 GiB on the declared total and answers 413 past it.\n *\n * @returns The tracked id of each file, in order.\n */\n addFiles(workspace: UploadTarget, files: File[], options: AddFilesOptions): string[] {\n if (!files.length) return [];\n\n // The three job-wide values go out as tus metadata on every file of the job.\n const jobId = generateUUID();\n const fileCount = files.length;\n const totalBytes = files.reduce((total, f) => total + f.size, 0);\n\n // The id is the key the tracker and this service follow the file by. It is NOT the\n // server's: the listing names a document `/DocUpload/…|<containerId>/<fileName>`.\n const items: UploadItem[] = files.map(file => ({ id: `${jobId}/${file.name}`, workspaceId: workspace.id }));\n\n this.tracker.track(\n files.map((file, i) => ({ id: items[i].id, name: file.name, type: getDocumentType(file.name) })),\n workspace.id,\n workspace.name\n );\n items.forEach((item, i) => {\n this.inFlight.add(item.id);\n this.startUpload(item, files[i], { jobId, fileCount, totalBytes }, options.endpoint);\n });\n this.poller.start();\n\n return items.map(item => item.id);\n }\n\n /**\n * Abandons a transfer and forgets it: stops it, and drops the file from the\n * tracker. Works while it is sending\n * and once it has failed; once the bytes are in, indexing is the server's and\n * there is nothing left to cancel.\n *\n * The server is asked to discard what it received (tus termination extension).\n * It may not support that, in which case the transfer is only stopped and the\n * leftover is reclaimed by the server's own retention.\n */\n async cancel(id: string): Promise<void> {\n const entry = this.uploads.get(id);\n if (!entry) return;\n\n // Forget it first: nothing the stopping transfer may still report should touch the state again.\n this.uploads.delete(id);\n this.inFlight.delete(id);\n this.clearRetryNotice(id);\n this.tracker.remove(id);\n\n try {\n await entry.handle.abort(true);\n } catch {\n await entry.handle.abort(false).catch(() => undefined);\n }\n }\n\n /** Sends a failed transfer again, from where the server left off. Only a transfer the server did not refuse. */\n retry(id: string): void {\n const entry = this.uploads.get(id);\n const upload = this.tracker.uploads().find(u => u.id === id);\n if (!entry || entry.refused || upload?.status !== \"error\") return;\n\n this.tracker.restart(id);\n this.inFlight.add(id);\n entry.handle.resume();\n this.poller.start();\n }\n\n /**\n * Picks up every transfer whose retry budget ran out while the connection was\n * down. Safe to call at any time: a transfer still retrying is left alone.\n * The `File` is held by tus for as long as the tab is open, which is what makes\n * this recovery automatic — an upload does not survive the tab closing.\n */\n resumeStalled(): void {\n for (const id of this.uploads.keys()) this.retry(id);\n }\n\n /**\n * Tells the service which workspace is open, so the status polling can be\n * scoped to it (the API documents that as the cheaper lookup) and re-read its\n * listing. `null` when none is. Starts the polling: a page reload loses every\n * local tus handle and the listing no longer says what is in progress, so the\n * status API is the only way to know. The loop stops by itself after one tick\n * when nothing is.\n */\n watch(workspaceId: string | null): void {\n this.focusedWorkspaceId = workspaceId;\n if (workspaceId) this.poller.start();\n }\n\n startPolling(): void {\n this.poller.start();\n }\n\n stopPolling(): void {\n this.poller.stop();\n }\n\n // ── sending ──\n\n private startUpload(item: UploadItem, file: File, job: { jobId: string; fileCount: number; totalBytes: number }, endpoint: string): void {\n const workspaceId = item.workspaceId;\n if (!endpoint) {\n this.inFlight.delete(item.id);\n this.markError(item);\n return;\n }\n\n const entry: UploadEntry = {\n workspaceId,\n refused: false,\n handle: this.createUpload(file, {\n endpoint: resolveEndpointUrl(endpoint).href,\n // The ingest endpoint validates exactly these keys and answers 400 if one is\n // missing. `totalBytes` and `fileCount` describe the WHOLE job, not this\n // file — identical on every upload of the job — so the server knows how much\n // to expect before it is complete.\n metadata: { containerId: workspaceId, jobId: job.jobId, fileName: file.name, totalBytes: String(job.totalBytes), fileCount: String(job.fileCount) },\n events: {\n shouldRetry: (status, attempt) => {\n if (status === 401) {\n // Recoverable, not fatal — unless a recovery has already come back\n // \"genuinely signed out\", in which case another attempt is just another\n // 401. Stop there: the upload lands in the error state and is picked up\n // again once the user is back.\n void this.recoverSessionAndResume();\n if (this.signedOut) {\n this.clearRetryNotice(item.id);\n return false;\n }\n } else if (FATAL_UPLOAD_STATUSES.has(status)) {\n entry.refused = true;\n this.clearRetryNotice(item.id);\n return false;\n }\n // tus waits retryDelays[attempt] before this attempt and passes that same\n // index here, so the banner's countdown is the real delay, not an estimate.\n this.noteRetryScheduled(item.id, attempt);\n return true;\n },\n // Anything that came back cleanly means the connection works again for this\n // upload, whatever the retry bookkeeping still says.\n responded: status => {\n if (status < 400) this.noteHealthy(item.id);\n },\n // A recovery kicked off by a previous 401 may still be in flight, and\n // carrying the token it is fetching is the entire point of this retry.\n beforeRequest: async () => {\n if (this.recovery) await this.recovery;\n },\n progress: (bytesUploaded, bytesTotal) => {\n // The browser reports bytes handed to the socket, not bytes the server\n // processed: on a fast transfer a stale event can still arrive after\n // success moved the file to indexing. Drop anything that comes once the\n // upload phase is over, so it cannot stomp the indexing percentage.\n if (this.tracker.uploads().find(u => u.id === item.id)?.status !== \"uploading\") return;\n this.tracker.setProgress(item.id, bytesTotal ? (bytesUploaded / bytesTotal) * 100 : 0);\n },\n success: () => {\n // Bytes are in; indexing is now the server's business, so the poller owns\n // this document from here. Nothing left to resume.\n this.noteHealthy(item.id);\n this.uploads.delete(item.id);\n this.markIndexing(item);\n this.poller.start();\n },\n failed: () => {\n // The handle is kept: the retry budget is spent, but the upload is not lost —\n // `retry()` can pick it up again and tus resumes from the server's offset.\n this.clearRetryNotice(item.id);\n this.inFlight.delete(item.id);\n this.markError(item, { retryable: !entry.refused });\n }\n }\n })\n };\n\n this.uploads.set(item.id, entry);\n entry.handle.begin();\n }\n\n // ── what a file's state change writes ──\n\n private markIndexing(item: UploadItem): void {\n this.tracker.setIndexing(item.id);\n }\n\n private markError(item: UploadItem, options: { retryable?: boolean } = {}): void {\n this.tracker.setError(item.id, options);\n }\n\n // ── following indexing ──\n //\n // - GET .../agent-workspace/status -> the tracker: aggregate, errors[] and rejected[] PER JOB\n // - GET .../container/{id}/documents -> the workspace's list, which is the index and nothing else\n\n private async pollUploadStatus(signal: AbortSignal): Promise<PollResult> {\n try {\n const result = await this.runPollTick(signal);\n this.noteHealthy();\n return result;\n } catch (error) {\n // A 401 means the session lapsed, and every further tick would be another 401\n // at the poller's own pace. Park the loop on the recovery instead: if it works\n // the loop carries on (pending: true) and the next tick does the real work; if\n // not, report nothing pending so the poller stops, and leave restarting to the\n // `authenticated` listener or the banner's Retry button.\n if (error instanceof ApiError && error.status === 401) {\n return { pending: await this.recoverSessionAndResume(), changed: false };\n }\n throw error;\n }\n }\n\n private async runPollTick(signal: AbortSignal): Promise<PollResult> {\n const workspaceId = this.focusedWorkspaceId;\n\n // Scoped to the open workspace when there is one, and unscoped otherwise, so\n // the tracker still follows a batch after the user navigates away from it.\n const response = await fetchUploadStatus(workspaceId ? { workspaceId } : {}, { signal });\n const jobs = allJobs(response);\n\n // Jobs started elsewhere come first, so that `applyJobs` finds their rows to update.\n const remote = await this.remoteJobs(response, signal);\n let changed = this.tracker.applyRemoteJobs(remote);\n changed = this.tracker.applyJobs(jobs) || changed;\n this.settleFinishedUploads();\n const pending = jobs.some(job => !isJobTerminal(job));\n\n // The list follows the index, not the status: a file shows up once the listing returns it.\n // The last tick of a loop reads it too, which is when a file that just got indexed comes in.\n if (workspaceId) changed = (await this.refreshListing(workspaceId, signal)) || changed;\n\n return { pending: pending || this.inFlight.size > 0, changed };\n }\n\n /**\n * The jobs of the response the panel should show although this client did not send\n * them. The server decides which ones the response holds — those in progress or just\n * ended, on the workspaces the user can write to — so none is filtered by role or date\n * here; only, with a workspace open, those that belong to it are kept. Whether the user\n * started one is read from `initiatedBy`, which is their `userId`, so it holds after a\n * page reload too.\n */\n private async remoteJobs(response: UploadStatusResponse, signal: AbortSignal): Promise<RemoteJobEntry[]> {\n const me = (await this.currentUser.whenReady())?.id;\n if (signal.aborted) return [];\n\n const entries: RemoteJobEntry[] = [];\n for (const { job, workspaceId, workspaceName: apiName } of workspaceJobs(response)) {\n if (this.focusedWorkspaceId && this.focusedWorkspaceId !== workspaceId) continue;\n const workspaceName = this.workspaces.workspace(workspaceId)?.name ?? apiName;\n entries.push({ job, workspaceId, workspaceName, mine: !!me && job.initiatedBy === me });\n }\n return entries;\n }\n\n /** Re-reads the workspace's listing. @returns Whether it differs from what was shown. */\n private async refreshListing(workspaceId: string, signal: AbortSignal): Promise<boolean> {\n const before = JSON.stringify(this.workspaces.documentsOf(workspaceId));\n await this.workspaces.loadDocuments(workspaceId, signal);\n return JSON.stringify(this.workspaces.documentsOf(workspaceId)) !== before;\n }\n\n /** The job-level status can finish a file this client was still holding the poller for: release it. */\n private settleFinishedUploads(): void {\n for (const upload of this.tracker.uploads()) {\n if (upload.status === \"done\") this.inFlight.delete(upload.id);\n }\n }\n\n // ── connection loss & session recovery ──\n //\n // The tus transfers and the status poll stall on the same cause, so one state\n // machine owns both and one banner speaks for both: what is waiting, for how\n // long, and whether the wait is worth anything.\n\n /** Transfers that spent their whole budget and are parked in the error state: what \"N uploads paused\" counts once retrying has stopped. */\n private stalledUploadCount(): number {\n return this.tracker.uploads().filter(u => u.status === \"error\" && u.retryable).length;\n }\n\n private publishConnection(): void {\n if (!this.retrying.size && !this.recovering && !this.signedOut) {\n this.lostSinceMs = 0;\n this.tracker.setConnection(null);\n return;\n }\n if (!this.lostSinceMs) this.lostSinceMs = Date.now();\n\n // The soonest retry is the one the countdown is about: it is the next thing that will actually happen.\n const soonest = [...this.retrying.values()].reduce<RetrySchedule | null>((best, r) => (best === null || r.retryAtMs < best.retryAtMs ? r : best), null);\n\n const base = {\n uploads: this.retrying.size || this.stalledUploadCount(),\n maxAttempts: UPLOAD_RETRY_DELAYS.length,\n sinceMs: this.lostSinceMs\n };\n\n let notice: ConnectionNotice;\n if (this.recovering) {\n notice = { ...base, phase: \"reconnecting\", attempt: soonest ? soonest.attempt + 1 : 0 };\n } else if (this.signedOut || !soonest) {\n notice = {\n ...base,\n phase: \"signed-out\",\n attempt: soonest ? soonest.attempt + 1 : 0,\n retry: () => {\n void this.recoverSessionAndResume();\n }\n };\n } else {\n notice = {\n ...base,\n phase: \"retrying\",\n // retryAttempt is 0-based; humans count attempts from 1.\n attempt: soonest.attempt + 1,\n retryAtMs: soonest.retryAtMs,\n // What is left of the budget after this attempt, measured from it: when tus will stop trying on its own.\n budgetEndsAtMs: soonest.retryAtMs + UPLOAD_RETRY_DELAYS.slice(soonest.attempt + 1).reduce((a, b) => a + b, 0)\n };\n }\n this.tracker.setConnection(notice);\n }\n\n private noteRetryScheduled(id: string, attempt: number): void {\n this.retrying.set(id, { attempt, retryAtMs: Date.now() + (UPLOAD_RETRY_DELAYS[attempt] ?? 0) });\n this.publishConnection();\n }\n\n /** Drops a scheduled retry — the attempt happened, or will never happen. */\n private clearRetryNotice(id: string): void {\n if (this.retrying.delete(id)) this.publishConnection();\n }\n\n /**\n * Something came back cleanly, so the session demonstrably works — whatever the\n * last recovery concluded. Without this, one `signed-out` verdict would outlive\n * the sign-in that fixed it and make the NEXT 401, on a healthy session, refuse\n * to retry.\n */\n private noteHealthy(id?: string): void {\n const hadRetry = id !== undefined && this.retrying.delete(id);\n const wasSignedOut = this.signedOut;\n this.signedOut = false;\n if (hadRetry || wasSignedOut) this.publishConnection();\n }\n\n /**\n * Recovers the session and picks up whatever was waiting on it. Single-flight:\n * every stalled upload and the status poll hit 401 within the same instant, and\n * one probe answers for all of them.\n */\n private recoverSessionAndResume(): Promise<boolean> {\n this.recovery ??= this.runRecovery().finally(() => {\n this.recovery = null;\n });\n return this.recovery;\n }\n\n private async runRecovery(): Promise<boolean> {\n this.recovering = true;\n this.publishConnection();\n const ok = await recoverSession();\n this.recovering = false;\n this.signedOut = !ok;\n if (ok) {\n // Uploads still inside their retry budget need nothing: the headers are read per\n // attempt, so the refreshed token is already theirs. Only the ones that ran out\n // have to be pushed.\n this.resumeStalled();\n this.poller.start();\n }\n this.publishConnection();\n return ok;\n }\n}\n","import { inject, Injectable, signal } from \"@angular/core\";\nimport { fetchPrincipalsByIds, searchPrincipals } from \"@sinequa/atomic\";\nimport type { SecurityPrincipal } from \"@sinequa/atomic\";\nimport { CurrentUserService } from \"./current-user.service\";\n\nexport interface PrincipalSummary {\n name: string;\n isGroup: boolean;\n /** Users only, and only when the directory has one — the access list's secondary line. */\n email?: string;\n}\n\n/** A principal id turned into something renderable. */\nexport interface ResolvedPrincipal {\n /** Display label — the cached name, or the raw id when it isn't cached. Never decorated: see `isSelf`. */\n name: string;\n /** Secondary line under the name, when the directory gave one. */\n email?: string;\n isGroup: boolean;\n /** False when the id has no cached name and `name` is the raw id. */\n resolved: boolean;\n /** True when this id is the logged-in user's. */\n isSelf: boolean;\n}\n\nconst STORAGE_KEY = \"document-workspaces:principal-name-cache\";\n\n/**\n * The label to show for a principal: a user's full name where it has one,\n * otherwise the account name. Groups only ever have `name`.\n */\nexport function principalDisplayName(principal: SecurityPrincipal): string {\n return principal.isUser ? principal.fullName || principal.name : principal.name;\n}\n\nfunction readStorage(): Record<string, PrincipalSummary> {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n return raw ? JSON.parse(raw) : {};\n } catch {\n return {};\n }\n}\n\nfunction writeStorage(map: Record<string, PrincipalSummary>): void {\n try {\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(map));\n } catch {\n // sessionStorage unavailable (private browsing, quota) — the cache just\n // won't persist across a reload; not worth surfacing an error for.\n }\n}\n\n/**\n * Names for the principal ids a workspace's owners/readers hold, which are\n * opaque `domain|id` strings the API never resolves for us.\n *\n * Persisted in `sessionStorage` per the product decision — it survives\n * navigation within the tab and is cleared when the tab closes — so an access\n * list doesn't flash raw ids on every open.\n *\n * `resolve()` is synchronous and reads signals, so a template or `computed`\n * that calls it re-renders by itself once {@link ensureResolved} fills a gap.\n * An id that is not cached shows up as the raw id, which is also what an id\n * the server can't resolve at all (a deleted account) keeps showing — still\n * enough to keep it in the owners/readers arrays on the next save rather than\n * silently revoking it.\n */\n@Injectable({ providedIn: \"root\" })\nexport class PrincipalDirectory {\n private readonly currentUser = inject(CurrentUserService);\n private readonly cache = signal<Record<string, PrincipalSummary>>(readStorage());\n\n /**\n * Resolves a principal id for display, identically wherever an owners/readers\n * id is rendered — the access list and the add/edit dialog's \"Share with\" chips.\n *\n * \"(you)\" is deliberately NOT folded into the name: it would end up in the\n * avatar's initials and in every message that interpolates a name. Views\n * render it as its own element off `isSelf`.\n */\n resolve(id: string): ResolvedPrincipal {\n const user = this.currentUser.user();\n const isSelf = !!user && user.id === id;\n const cached: PrincipalSummary | undefined = isSelf ? { name: user.name, isGroup: false } : this.cache()[id];\n return {\n name: cached ? cached.name : id,\n email: cached?.email,\n isGroup: cached ? cached.isGroup : false,\n resolved: !!cached,\n isSelf\n };\n }\n\n /** Records a name learned elsewhere (e.g. a person just picked in the principal picker). */\n remember(id: string, summary: PrincipalSummary): void {\n this.cache.update(map => ({ ...map, [id]: summary }));\n writeStorage(this.cache());\n }\n\n /**\n * Searches the directory by free text and remembers every name it learns, so a\n * person just picked already has one when the access list shows them.\n *\n * @param query - What the user typed.\n * @param signal - Cancels the request when a newer keystroke supersedes it.\n * @returns Users and groups alike; the caller decides which it can use.\n */\n async search(query: string, signal?: AbortSignal): Promise<SecurityPrincipal[]> {\n const { principals = [] } = await searchPrincipals({ offset: 0, limit: 20, search: query }, signal);\n this.cache.update(map => {\n const next = { ...map };\n for (const p of principals) {\n next[p.userId] = { name: principalDisplayName(p), isGroup: p.isGroup, email: p.isUser ? p.email : undefined };\n }\n return next;\n });\n writeStorage(this.cache());\n return principals;\n }\n\n /**\n * Fetches and caches the names for any of `ids` not already known.\n *\n * @param ids - Principal identities, typically a workspace's owners/readers.\n *\n * @returns Resolves once the cache has been updated. A failed lookup resolves\n * rather than rejecting: the UI already renders unresolved ids, and a name\n * is not worth failing an access dialog over.\n */\n async ensureResolved(ids: string[]): Promise<void> {\n const known = this.cache();\n const missing = [...new Set(ids)].filter(id => id && !known[id]);\n if (!missing.length) return;\n try {\n const principals = await fetchPrincipalsByIds(missing);\n this.cache.update(map => {\n const next = { ...map };\n for (const p of principals) {\n next[p.userId] = { name: principalDisplayName(p), isGroup: p.isGroup, email: p.isUser ? p.email : undefined };\n }\n return next;\n });\n writeStorage(this.cache());\n } catch {\n /* leaves those ids showing as raw ids — see the class comment */\n }\n }\n}\n","import { booleanAttribute, Directive, input, output, signal } from \"@angular/core\";\n\n/**\n * Turns an element into a place to drop files on, or to click to pick some.\n *\n * ```html\n * <div dropzone (filesSelected)=\"stage($event)\">Drop your files here</div>\n * ```\n *\n * Both ways end in the same `filesSelected` output. While files are dragged over\n * it the host gets a `data-dragover` attribute to style. It is a button for\n * assistive technology and the keyboard (Enter or Space opens the file picker).\n *\n * It does no gatekeeping — no type, count or size check: whatever is dropped\n * or picked is emitted, and what can be handled is the server's call.\n */\n@Directive({\n selector: \"[dropzone]\",\n host: {\n role: \"button\",\n \"[attr.tabindex]\": \"disabled() ? -1 : 0\",\n \"[attr.aria-disabled]\": \"disabled() ? 'true' : null\",\n \"[attr.data-dragover]\": \"dragging() ? '' : null\",\n \"(click)\": \"browse()\",\n \"(keydown.enter)\": \"onKey($event)\",\n \"(keydown.space)\": \"onKey($event)\",\n \"(dragenter)\": \"onDragEnter($event)\",\n \"(dragover)\": \"onDragOver($event)\",\n \"(dragleave)\": \"onDragLeave($event)\",\n \"(drop)\": \"onDrop($event)\"\n }\n})\nexport class Dropzone {\n /** Files dropped or picked, in the order the browser gave them. */\n readonly filesSelected = output<File[]>();\n readonly disabled = input(false, { alias: \"dropzoneDisabled\", transform: booleanAttribute });\n\n protected readonly dragging = signal(false);\n\n // dragenter / dragleave also fire when the cursor crosses a child element, so a\n // bare boolean would flicker: count the elements entered and left instead.\n private depth = 0;\n\n protected browse(): void {\n if (this.disabled()) return;\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.multiple = true;\n input.addEventListener(\"change\", () => this.emit(input.files));\n input.click();\n }\n\n protected onKey(event: Event): void {\n event.preventDefault(); // Space would scroll the page\n this.browse();\n }\n\n protected onDragEnter(event: DragEvent): void {\n if (!this.carriesFiles(event)) return;\n event.preventDefault();\n this.depth++;\n this.dragging.set(true);\n }\n\n protected onDragOver(event: DragEvent): void {\n if (!this.carriesFiles(event)) return;\n event.preventDefault(); // without it the browser refuses the drop\n if (event.dataTransfer) event.dataTransfer.dropEffect = this.disabled() ? \"none\" : \"copy\";\n }\n\n protected onDragLeave(event: DragEvent): void {\n if (!this.carriesFiles(event)) return;\n this.depth = Math.max(0, this.depth - 1);\n if (this.depth === 0) this.dragging.set(false);\n }\n\n protected onDrop(event: DragEvent): void {\n if (!this.carriesFiles(event)) return;\n event.preventDefault();\n this.depth = 0;\n this.dragging.set(false);\n if (!this.disabled()) this.emit(event.dataTransfer?.files ?? null);\n }\n\n /** A text or a link being dragged around the page is none of our business. */\n private carriesFiles(event: DragEvent): boolean {\n return !!event.dataTransfer?.types?.includes(\"Files\");\n }\n\n private emit(files: FileList | null): void {\n if (files?.length) this.filesSelected.emit(Array.from(files));\n }\n}\n","import { computed, Directive, input, model, signal } from \"@angular/core\";\n\n/**\n * Turns an element into the drag handle of a resizable panel.\n *\n * The panel's width is two-way bound through the directive's own name:\n *\n * ```html\n * <aside [style.width.px]=\"width()\">\n * <div [(resizeHandle)]=\"width\" resizeMin=\"280\" [resizeMax]=\"maxWidth()\"></div>\n * </aside>\n * ```\n *\n * It follows the WAI-ARIA separator pattern: the handle is focusable and the\n * arrow keys resize it, so the panel stays usable without a pointer. The pointer\n * is captured on `pointerdown`, so a drag keeps going when the cursor leaves the\n * handle or crosses an iframe (which would otherwise swallow the events).\n */\n@Directive({\n selector: \"[resizeHandle]\",\n exportAs: \"resizeHandle\",\n host: {\n role: \"separator\",\n \"aria-orientation\": \"vertical\",\n tabindex: \"0\",\n \"[attr.aria-valuenow]\": \"width()\",\n \"[attr.aria-valuemin]\": \"resizeMin()\",\n \"[attr.aria-valuemax]\": \"resizeMax() ?? null\",\n \"[attr.data-resizing]\": \"resizing() ? '' : null\",\n \"(pointerdown)\": \"onPointerDown($event)\",\n \"(pointermove)\": \"onPointerMove($event)\",\n \"(pointerup)\": \"onPointerEnd($event)\",\n \"(pointercancel)\": \"onPointerEnd($event)\",\n \"(keydown)\": \"onKeydown($event)\"\n }\n})\nexport class ResizeHandle {\n /** The panel's width in px. */\n readonly width = model.required<number>({ alias: \"resizeHandle\" });\n readonly resizeMin = input(280, { transform: Number });\n /** Upper bound in px; unbounded when absent. */\n readonly resizeMax = input<number | undefined>(undefined);\n /** px moved per arrow key press. */\n readonly resizeStep = input(16, { transform: Number });\n /**\n * Which edge of the panel the handle sits on — and therefore which way a drag\n * widens it: dragging a `left` handle to the left widens the panel.\n */\n readonly resizeEdge = input<\"left\" | \"right\">(\"left\");\n\n /** Whether the handle is being dragged. */\n readonly resizing = signal(false);\n private readonly direction = computed(() => (this.resizeEdge() === \"left\" ? -1 : 1));\n\n private startX = 0;\n private startWidth = 0;\n\n protected onPointerDown(event: PointerEvent): void {\n if (event.button !== 0) return;\n event.preventDefault(); // no text selection while dragging\n (event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);\n this.startX = event.clientX;\n this.startWidth = this.width();\n this.resizing.set(true);\n }\n\n protected onPointerMove(event: PointerEvent): void {\n if (!this.resizing()) return;\n this.width.set(this.clamp(this.startWidth + this.direction() * (event.clientX - this.startX)));\n }\n\n protected onPointerEnd(event: PointerEvent): void {\n if (!this.resizing()) return;\n this.resizing.set(false);\n (event.currentTarget as HTMLElement).releasePointerCapture?.(event.pointerId);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const towardsRight = event.key === \"ArrowRight\" ? 1 : -1;\n this.width.set(this.clamp(this.width() + this.direction() * towardsRight * this.resizeStep()));\n }\n\n private clamp(value: number): number {\n return Math.min(Math.max(value, this.resizeMin()), this.resizeMax() ?? Number.POSITIVE_INFINITY);\n }\n}\n","import type { WorkspaceRole } from \"../models/workspace.model\";\n\n/**\n * Whether the role may change a workspace (edit, share, delete, upload).\n * An unknown role cannot: hiding an action for a moment is the softer failure\n * than offering a reader one they will be refused.\n */\nexport function canWrite(role: WorkspaceRole | null | undefined): boolean {\n return role === \"creator\" || role === \"owner\";\n}\n","import { computed, Directive, input } from \"@angular/core\";\nimport type { WorkspaceRole } from \"../models/workspace.model\";\nimport { canWrite } from \"../utils/workspace-role\";\n\n/**\n * Hides an element from anyone who cannot change the workspace — a reader, or\n * whoever the role is not known for yet. One rule for every edit / share /\n * delete / upload control, instead of the check being repeated on each.\n *\n * ```html\n * <button [writerOnly]=\"workspace().currentUserRole\" (click)=\"edit()\">Edit</button>\n * ```\n *\n * It sets `hidden`, which also drops the element from the accessibility tree\n * and the tab order; a layout class that sets `display` must not override it.\n */\n@Directive({\n selector: \"[writerOnly]\",\n host: { \"[hidden]\": \"!writable()\" }\n})\nexport class WriterOnly {\n readonly writerOnly = input.required<WorkspaceRole | null | undefined>();\n\n protected readonly writable = computed(() => canWrite(this.writerOnly()));\n}\n","/** A slice of a text, flagged when it is what a search matched. */\nexport interface TextChunk {\n text: string;\n match: boolean;\n}\n\n/** Lower-cases and strips accents, so \"Resume\" finds \"Résumé\". */\nexport function fold(text: string): string {\n return text.normalize(\"NFD\").replace(/\\p{M}/gu, \"\").toLowerCase();\n}\n\n/**\n * Folds `text` while remembering, for each folded code unit, which slice of the\n * original it came from — folding can change the length (a combining mark\n * vanishes, `İ` becomes two units), so a match position in the folded text is\n * not a position in the original.\n */\nexport function foldWithOffsets(text: string): { folded: string; starts: number[]; ends: number[] } {\n let folded = \"\";\n const starts: number[] = [];\n const ends: number[] = [];\n let index = 0;\n for (const char of text) {\n const start = index;\n index += char.length;\n const piece = fold(char);\n for (let i = 0; i < piece.length; i++) {\n starts.push(start);\n ends.push(index);\n }\n folded += piece;\n }\n return { folded, starts, ends };\n}\n\n/**\n * Whether `text` contains `query`, ignoring case and accents. An empty query\n * matches everything.\n */\nexport function matchesText(text: string, query: string): boolean {\n if (!query) return true;\n const folded = fold(query);\n return !folded || fold(text).includes(folded);\n}\n\n/**\n * Splits `text` around every occurrence of `query` (case- and accent-insensitive),\n * keeping the original characters — the accents a user typed or stored are not\n * lost to the matching.\n *\n * It returns data rather than markup on purpose: the text is user-supplied\n * (a workspace or document name) and the view renders the chunks through Angular's\n * own escaping instead of interpolating them into HTML.\n */\nexport function splitByMatch(text: string, query: string): TextChunk[] {\n if (!text) return [];\n const foldedQuery = query ? fold(query) : \"\";\n if (!foldedQuery) return [{ text, match: false }];\n\n const { folded, starts, ends } = foldWithOffsets(text);\n const chunks: TextChunk[] = [];\n let cursor = 0;\n let from = 0;\n\n for (let hit = folded.indexOf(foldedQuery, from); hit !== -1; hit = folded.indexOf(foldedQuery, from)) {\n const start = starts[hit];\n const end = ends[hit + foldedQuery.length - 1];\n if (start > cursor) chunks.push({ text: text.slice(cursor, start), match: false });\n chunks.push({ text: text.slice(start, end), match: true });\n cursor = end;\n from = hit + foldedQuery.length;\n }\n\n if (!chunks.length) return [{ text, match: false }];\n if (cursor < text.length) chunks.push({ text: text.slice(cursor), match: false });\n return chunks;\n}\n","import { Pipe, PipeTransform } from \"@angular/core\";\nimport { splitByMatch, TextChunk } from \"../utils/text-match\";\n\n/**\n * Splits a text around what a search matched, ignoring case and accents.\n *\n * Unlike `highlightWord`, it keeps the original characters (that pipe returns\n * chunks of the accent-stripped text, which would display \"Donnees\" for\n * \"Données\") and is meant to be rendered, never bound to `innerHTML`:\n *\n * ```html\n * @for (chunk of workspace.name | highlightMatch: query(); track $index) {\n * @if (chunk.match) { <mark>{{ chunk.text }}</mark> } @else { {{ chunk.text }} }\n * }\n * ```\n */\n@Pipe({ name: \"highlightMatch\" })\nexport class HighlightMatchPipe implements PipeTransform {\n transform(text: string | null | undefined, query: string | null | undefined): TextChunk[] {\n return splitByMatch(text ?? \"\", query ?? \"\");\n }\n}\n","import { Temporal } from \"temporal-polyfill\";\nimport type { Message } from \"./message\";\n\n/**\n * \"Added 3 hours ago\" for a document's creation date, as a {@link Message} the\n * template translates.\n *\n * The relative part comes from `Intl.RelativeTimeFormat` rather than a catalog\n * entry per unit: one call localizes the number, the unit and the \"ago\" pattern,\n * and `numeric: \"auto\"` gives idiomatic forms (\"yesterday\", \"last week\") that a\n * per-unit catalog would have to spell out for every language.\n *\n * @param iso - ISO 8601 timestamp (`createdAt`).\n * @param lang - The active language.\n * @param now - Reference instant, injectable for tests.\n * @returns `null` when `iso` is missing or unparseable.\n */\nexport function relativeDateMessage(iso: string | undefined, lang: string, now: Temporal.Instant = Temporal.Now.instant()): Message | null {\n if (!iso) return null;\n let created: Temporal.Instant;\n try {\n created = Temporal.Instant.from(iso);\n } catch {\n return null;\n }\n\n // A date slightly in the future (clock skew between server and browser) reads as \"just now\".\n const elapsed = Temporal.Instant.compare(created, now) > 0 ? Temporal.Duration.from({ seconds: 0 }) : created.until(now, { largestUnit: \"hour\" });\n const minutes = Math.floor(elapsed.total(\"minute\"));\n // \"now\" reads oddly next to \"Added\", so the sub-minute case has its own entry.\n if (minutes < 1) return { key: \"documentWorkspaces.format.addedJustNow\" };\n\n const formatter = new Intl.RelativeTimeFormat(lang, { numeric: \"auto\" });\n const hours = Math.floor(elapsed.total(\"hour\"));\n const days = Math.floor(hours / 24);\n let relative: string;\n if (minutes < 60) relative = formatter.format(-minutes, \"minute\");\n else if (hours < 24) relative = formatter.format(-hours, \"hour\");\n else if (days < 7) relative = formatter.format(-days, \"day\");\n else if (days < 30) relative = formatter.format(-Math.floor(days / 7), \"week\");\n else relative = formatter.format(-Math.floor(days / 30), \"month\");\n\n return { key: \"documentWorkspaces.format.added\", params: { relative } };\n}\n\n/** `1.2 MB`, `340 KB`, `12 byte` — in the active language. Bytes, KB (rounded, at least 1), then MB. */\nexport function formatFileSize(bytes: number, lang: string): string {\n if (bytes < 1024) return new Intl.NumberFormat(lang, { style: \"unit\", unit: \"byte\" }).format(bytes);\n if (bytes < 1024 * 1024) {\n return new Intl.NumberFormat(lang, { style: \"unit\", unit: \"kilobyte\" }).format(Math.max(1, Math.round(bytes / 1024)));\n }\n return new Intl.NumberFormat(lang, { style: \"unit\", unit: \"megabyte\", maximumFractionDigits: 1 }).format(bytes / (1024 * 1024));\n}\n\n/** A date for a label — \"Sep 30, 2026\" — in the active language, or `null` when there is none or it cannot be read. */\nexport function formatDate(iso: string | undefined, lang: string): string | null {\n if (!iso) return null;\n try {\n return Temporal.Instant.from(iso).toLocaleString(lang, { dateStyle: \"medium\" });\n } catch {\n return null;\n }\n}\n","import { assertInInjectionContext, computed, inject, Signal } from \"@angular/core\";\nimport { toSignal } from \"@angular/core/rxjs-interop\";\nimport { TranslocoService } from \"@jsverse/transloco\";\nimport type { WorkspaceDocument } from \"../models/workspace.model\";\nimport { formatFileSize, relativeDateMessage } from \"../utils/format\";\nimport type { Message } from \"../utils/message\";\n\nexport interface DocumentLabels {\n /** \"1.2 MB\", or `null` when the size is not known — the API does not serve one, only a file uploaded in this session has it. */\n size: Signal<string | null>;\n /** \"Added 3 hours ago\", or `null` when the creation date is not known. */\n added: Signal<Message | null>;\n}\n\n/**\n * The size and date labels a document card and a document row both show,\n * following the active language.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectDocumentLabels(document: Signal<WorkspaceDocument>): DocumentLabels {\n assertInInjectionContext(injectDocumentLabels);\n const transloco = inject(TranslocoService);\n const lang = toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });\n\n return {\n size: computed(() => {\n const { size } = document();\n return size === undefined ? null : formatFileSize(size, lang());\n }),\n added: computed(() => relativeDateMessage(document().createdAt, lang()))\n };\n}\n","import { assertInInjectionContext, DestroyRef, inject, Signal, signal } from \"@angular/core\";\n\n/**\n * Whether the browser reports a network connection, kept up to date from the\n * window's `online` / `offline` events. `true` where there is no `navigator`\n * (server-side rendering).\n *\n * `navigator.onLine` only knows about the network interface, not whether the\n * server is reachable: treat `false` as certain and `true` as likely.\n *\n * Must be called from an injection context; the listeners are removed when it is destroyed.\n */\nexport function injectOnlineStatus(): Signal<boolean> {\n assertInInjectionContext(injectOnlineStatus);\n\n const online = signal(typeof navigator === \"undefined\" ? true : navigator.onLine);\n\n if (typeof window !== \"undefined\") {\n const goOnline = () => online.set(true);\n const goOffline = () => online.set(false);\n window.addEventListener(\"online\", goOnline);\n window.addEventListener(\"offline\", goOffline);\n inject(DestroyRef).onDestroy(() => {\n window.removeEventListener(\"online\", goOnline);\n window.removeEventListener(\"offline\", goOffline);\n });\n }\n\n return online.asReadonly();\n}\n","import { assertInInjectionContext, computed, Signal, signal } from \"@angular/core\";\n\nexport interface SelectionOptions<T> {\n /** What can currently be selected — pass the list as it is displayed (filtered), not the full one. */\n items: Signal<readonly T[]>;\n /** A stable id per item. */\n key: (item: T) => string;\n /**\n * Whether an item may be ticked at all — a workspace shared read-only cannot be\n * deleted, so it should not count towards \"3 selected\" nor be ticked by \"select all\".\n * Every item is selectable when absent. Read reactively: it may depend on signals.\n */\n selectable?: (item: T) => boolean;\n}\n\nexport interface Selection<T> {\n /** Whether the selection mode (checkboxes, selection bar) is on. */\n readonly active: Signal<boolean>;\n /** The displayed items that can be ticked — empty when there is nothing to offer the selection mode for. */\n readonly selectableItems: Signal<T[]>;\n /** The displayed, selectable items that are ticked. An item filtered out of `items` is not counted. */\n readonly selectedItems: Signal<T[]>;\n readonly count: Signal<number>;\n /** Every selectable displayed item is ticked (never true when there is none). */\n readonly allSelected: Signal<boolean>;\n /** Some, but not all, are ticked — the \"indeterminate\" state of a select-all checkbox. */\n readonly partiallySelected: Signal<boolean>;\n /** Whether the item may be ticked. Reactive when read from a template or a `computed`. */\n canSelect(item: T): boolean;\n /** Reactive when read from a template or a `computed`. An item that cannot be selected is never selected. */\n isSelected(item: T): boolean;\n /** Does nothing for an item that cannot be selected. */\n toggle(item: T, selected?: boolean): void;\n /** Ticks every selectable displayed item, or unticks them all when they already are. */\n toggleAll(): void;\n /** Turns the selection mode on or off; leaving it clears the selection. */\n toggleMode(): void;\n start(): void;\n /** Leaves the selection mode and clears the selection. */\n exit(): void;\n clear(): void;\n}\n\n/**\n * Multi-selection over a displayed list — the selection mode, the ticked\n * items, and the state of a select-all checkbox — shared by the workspace list\n * and the document list.\n *\n * Ticks are kept by key, so they survive the list being filtered or switched\n * between grid and list views: a tick on an item that is filtered out stays\n * until it comes back, but only displayed items are counted and returned.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectSelection<T>(options: SelectionOptions<T>): Selection<T> {\n assertInInjectionContext(injectSelection);\n\n const { items, key } = options;\n const canSelect = options.selectable ?? (() => true);\n const active = signal(false);\n const keys = signal<ReadonlySet<string>>(new Set());\n\n const selectableItems = computed(() => items().filter(canSelect));\n const selectedItems = computed(() => selectableItems().filter(item => keys().has(key(item))));\n const count = computed(() => selectedItems().length);\n // `length > 0` matters: [].every() is true, which would label an empty list \"Unselect all\".\n const allSelected = computed(() => selectableItems().length > 0 && count() === selectableItems().length);\n const partiallySelected = computed(() => count() > 0 && !allSelected());\n\n const clear = () => keys.set(new Set());\n\n return {\n active: active.asReadonly(),\n selectableItems,\n selectedItems,\n count,\n allSelected,\n partiallySelected,\n canSelect,\n isSelected: item => canSelect(item) && keys().has(key(item)),\n toggle: (item, selected) => {\n if (!canSelect(item)) return;\n const id = key(item);\n keys.update(current => {\n const next = new Set(current);\n const on = selected ?? !next.has(id);\n if (on) next.add(id);\n else next.delete(id);\n return next;\n });\n },\n toggleAll: () => {\n const displayed = selectableItems();\n if (allSelected()) {\n const ids = new Set(displayed.map(key));\n keys.update(current => new Set([...current].filter(id => !ids.has(id))));\n } else {\n keys.update(current => new Set([...current, ...displayed.map(key)]));\n }\n },\n toggleMode: () => {\n if (active()) {\n active.set(false);\n clear();\n } else {\n active.set(true);\n }\n },\n start: () => active.set(true),\n exit: () => {\n active.set(false);\n clear();\n },\n clear\n };\n}\n","import { fold, foldWithOffsets } from \"./text-match\";\n\n/** Elements whose text is not the document's own, or never shown. */\nconst SKIPPED = new Set([\"SCRIPT\", \"STYLE\", \"NOSCRIPT\", \"TEMPLATE\", \"HEAD\", \"TITLE\"]);\n\n/** Elements that start a new line of text: a match never reaches across one. */\nconst BLOCKS = new Set([\n \"ADDRESS\",\n \"ARTICLE\",\n \"ASIDE\",\n \"BLOCKQUOTE\",\n \"BR\",\n \"DD\",\n \"DIV\",\n \"DL\",\n \"DT\",\n \"FIGCAPTION\",\n \"FIGURE\",\n \"FOOTER\",\n \"H1\",\n \"H2\",\n \"H3\",\n \"H4\",\n \"H5\",\n \"H6\",\n \"HEADER\",\n \"HR\",\n \"LI\",\n \"MAIN\",\n \"NAV\",\n \"OL\",\n \"P\",\n \"PRE\",\n \"SECTION\",\n \"TABLE\",\n \"TD\",\n \"TH\",\n \"TR\",\n \"UL\"\n]);\n\n/** Stands between two blocks in the searched text. A query cannot contain it, so no match spans it. */\nconst BOUNDARY = \"\\u0000\";\n\nconst SHOW_TEXT = 4; // NodeFilter.SHOW_TEXT — the constant of the window the root belongs to may not be reachable\n\ninterface Segment {\n node: Text;\n /** Where this node's text starts in the concatenated, folded text. */\n from: number;\n length: number;\n /** For each folded unit, the slice of the node's own text it came from. */\n starts: number[];\n ends: number[];\n}\n\nfunction blockOf(node: Node, root: Node): Node {\n for (let element = node.parentElement; element && element !== root; element = element.parentElement) {\n if (BLOCKS.has(element.tagName)) return element;\n }\n return root;\n}\n\n/**\n * Every occurrence of `query` in the text under `root`, as DOM ranges — case- and\n * accent-insensitive, and across inline elements (`foo<b>bar</b>` holds \"foobar\").\n * The document itself is not touched: what to do with the ranges is the caller's\n * business (see `injectTextSearch`, which paints them with the CSS Custom Highlight API).\n *\n * The query is matched as typed, spaces included, against the text as the DOM holds\n * it: a run of whitespace in the source is not collapsed the way the browser draws it.\n */\nexport function findTextRanges(root: Node, query: string): Range[] {\n const needle = fold(query.trim());\n const doc = root.ownerDocument ?? (root as Document);\n if (!needle) return [];\n\n const segments: Segment[] = [];\n let folded = \"\";\n let previousBlock: Node | null = null;\n\n const walker = doc.createTreeWalker(root, SHOW_TEXT);\n for (let node = walker.nextNode() as Text | null; node; node = walker.nextNode() as Text | null) {\n const parent = node.parentElement;\n if (parent && SKIPPED.has(parent.tagName)) continue;\n const block = blockOf(node, root);\n if (previousBlock && block !== previousBlock) folded += BOUNDARY;\n previousBlock = block;\n\n const piece = foldWithOffsets(node.data);\n segments.push({ node, from: folded.length, length: piece.folded.length, starts: piece.starts, ends: piece.ends });\n folded += piece.folded;\n }\n\n const ranges: Range[] = [];\n let cursor = 0; // matches come in order, so the segment lookup only ever moves forward\n const segmentAt = (position: number): Segment => {\n while (cursor < segments.length - 1 && position >= segments[cursor].from + segments[cursor].length) cursor++;\n return segments[cursor];\n };\n\n for (let at = folded.indexOf(needle); at !== -1; at = folded.indexOf(needle, at + needle.length)) {\n cursor = 0;\n const first = segmentAt(at);\n const last = segmentAt(at + needle.length - 1);\n const range = doc.createRange();\n range.setStart(first.node, first.starts[at - first.from]);\n range.setEnd(last.node, last.ends[at + needle.length - 1 - last.from]);\n ranges.push(range);\n }\n return ranges;\n}\n","import { assertInInjectionContext, computed, DestroyRef, effect, inject, Signal, signal, untracked } from \"@angular/core\";\nimport { findTextRanges } from \"../utils/text-search\";\n\nexport const SEARCH_HIGHLIGHT = \"workspace-search\";\nexport const ACTIVE_HIGHLIGHT = \"workspace-search-active\";\n\nconst STYLE = `\n ::highlight(${SEARCH_HIGHLIGHT}) { background-color: rgba(250, 204, 21, 0.4); color: inherit; }\n ::highlight(${ACTIVE_HIGHLIGHT}) { background-color: rgba(249, 115, 22, 0.6); color: inherit; }\n`;\n\nexport interface TextSearchOptions {\n /** The node whose text is searched — the body of a same-origin iframe, say. `null` when there is nothing to search. */\n root: Signal<Node | null>;\n query: Signal<string>;\n /** How long the query must stay unchanged before the text is searched, in ms. */\n debounce?: number;\n}\n\nexport interface TextSearch {\n /** How many occurrences there are. */\n readonly matches: Signal<number>;\n /** The occurrence in focus (0-based), or -1 when there is none. */\n readonly activeIndex: Signal<number>;\n /** Whether the browser can paint the matches (the CSS Custom Highlight API); when it cannot, they are still counted and scrolled to. */\n readonly highlighting: Signal<boolean>;\n /** Moves to the next occurrence, wrapping round. */\n next(): void;\n previous(): void;\n}\n\n/** The painting half of the CSS Custom Highlight API, of the window the searched text lives in. */\nfunction highlighterOf(root: Node): { registry: HighlightRegistry; create: (ranges: Range[]) => Highlight; document: Document } | null {\n const document = root.ownerDocument ?? (root as Document);\n const win = document.defaultView as (Window & typeof globalThis) | null;\n const registry = win?.CSS?.highlights;\n if (!win || !registry || typeof win.Highlight !== \"function\") return null;\n return { registry, create: ranges => new win.Highlight(...ranges), document };\n}\n\n/** The rules that paint a highlight live in the document that holds the text — an iframe's own, not the host's. */\nfunction ensureStyle(document: Document): void {\n if (document.querySelector(\"style[data-workspace-search]\") || !document.head) return;\n const style = document.createElement(\"style\");\n style.setAttribute(\"data-workspace-search\", \"\");\n style.textContent = STYLE;\n document.head.appendChild(style);\n}\n\n/**\n * Find-in-text over a document you do not render through Angular, painted with the\n * CSS Custom Highlight API: the text is never touched, the matches are ranges the\n * browser draws.\n *\n * It follows the query (debounced) and the root: a new root is searched again, and\n * whatever was painted on the old one is taken back. Without the API (older\n * browsers) the occurrences are still counted and scrolled to.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectTextSearch({ root, query, debounce = 200 }: TextSearchOptions): TextSearch {\n assertInInjectionContext(injectTextSearch);\n\n const ranges = signal<Range[]>([]);\n const activeIndex = signal(-1);\n const highlighting = signal(false);\n const matches = computed(() => ranges().length);\n\n let painted: HighlightRegistry | null = null;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const unpaint = () => {\n painted?.delete(SEARCH_HIGHLIGHT);\n painted?.delete(ACTIVE_HIGHLIGHT);\n painted = null;\n };\n\n const paint = (node: Node | null, found: Range[], active: number) => {\n unpaint();\n const highlighter = node ? highlighterOf(node) : null;\n highlighting.set(!!highlighter);\n if (!highlighter || !found.length) return;\n ensureStyle(highlighter.document);\n highlighter.registry.set(SEARCH_HIGHLIGHT, highlighter.create(found));\n if (active >= 0) highlighter.registry.set(ACTIVE_HIGHLIGHT, highlighter.create([found[active]]));\n painted = highlighter.registry;\n };\n\n const reveal = (range: Range | undefined) => {\n // The container of the text, not the text: a node cannot be scrolled to.\n range?.startContainer.parentElement?.scrollIntoView?.({ block: \"center\" });\n };\n\n const run = () => {\n const node = root();\n const found = node ? findTextRanges(node, query()) : [];\n ranges.set(found);\n activeIndex.set(found.length ? 0 : -1);\n paint(node, found, activeIndex());\n reveal(found[0]);\n };\n\n effect(() => {\n root();\n const value = query();\n clearTimeout(timer);\n // An emptied box clears at once; typing waits for a pause.\n timer = setTimeout(() => untracked(run), value.trim() ? debounce : 0);\n });\n\n inject(DestroyRef).onDestroy(() => {\n clearTimeout(timer);\n unpaint();\n });\n\n const move = (step: 1 | -1) => {\n const count = ranges().length;\n if (!count) return;\n const next = (activeIndex() + step + count) % count;\n activeIndex.set(next);\n paint(root(), ranges(), next);\n reveal(ranges()[next]);\n };\n\n return { matches, activeIndex: activeIndex.asReadonly(), highlighting: highlighting.asReadonly(), next: () => move(1), previous: () => move(-1) };\n}\n","import { assertInInjectionContext, computed, Signal, signal } from \"@angular/core\";\nimport type { Workspace } from \"../models/workspace.model\";\nimport { matchesText } from \"../utils/text-match\";\nimport { canWrite } from \"../utils/workspace-role\";\n\n/** `editor`: the workspaces you can change (creator or owner); `reader`: those shared with you read-only. */\nexport type WorkspaceTab = \"all\" | \"editor\" | \"reader\";\n\nexport interface WorkspaceTabCounts {\n all: number;\n editor: number;\n reader: number;\n}\n\n/** What the search box looks at: the name only, or the name and the description. */\nexport type WorkspaceSearchFields = \"name\" | \"name+description\";\n\nexport interface WorkspaceFilterOptions {\n /** What the query is matched against. Read reactively; the name only when absent. */\n fields?: () => WorkspaceSearchFields;\n}\n\n/**\n * The workspace list's tab and search box, and the list they produce.\n *\n * The tab badges count the whole list: typing in the search box narrows what is\n * shown, not the numbers next to the tabs.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectWorkspaceFilter(workspaces: Signal<readonly Workspace[]>, options: WorkspaceFilterOptions = {}) {\n assertInInjectionContext(injectWorkspaceFilter);\n\n const tab = signal<WorkspaceTab>(\"all\");\n const query = signal(\"\");\n\n const counts = computed<WorkspaceTabCounts>(() => {\n const editor = workspaces().filter(w => canWrite(w.currentUserRole)).length;\n return { all: workspaces().length, editor, reader: workspaces().length - editor };\n });\n\n const filtered = computed(() =>\n workspaces().filter(w => {\n const inTab = tab() === \"all\" || (tab() === \"editor\") === canWrite(w.currentUserRole);\n const inQuery = matchesText(w.name, query()) || (options.fields?.() === \"name+description\" && matchesText(w.description, query()));\n return inTab && inQuery;\n })\n );\n\n return {\n tab,\n query,\n counts,\n filtered,\n clearQuery: () => query.set(\"\")\n };\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { splitByMatch } from \"../../utils/text-match\";\n\n/**\n * A text with what a search matched marked, ignoring case and accents.\n *\n * Rendered chunk by chunk through Angular's own escaping: the text is a user's\n * workspace or document name, and interpolating it into markup would execute\n * anything HTML-ish it contains. The template is on one line on purpose, so no\n * whitespace sneaks in between the chunks.\n */\n@Component({\n selector: \"highlighted-text\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"contents\" },\n template: `@for (chunk of chunks(); track $index) {@if (chunk.match) {<mark class=\"rounded-sm bg-[rgba(234,179,8,0.25)] text-inherit\">{{ chunk.text }}</mark>} @else {<span>{{ chunk.text }}</span>}}`\n})\nexport class HighlightedText {\n readonly text = input.required<string>();\n readonly query = input(\"\");\n\n protected readonly chunks = computed(() => splitByMatch(this.text(), this.query()));\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { UserIcon, UserRoundIcon } from \"@sinequa/galactik\";\nimport type { WorkspaceRole } from \"../../models/workspace.model\";\nimport { canWrite } from \"../../utils/workspace-role\";\n\n/** What the workspace is to you: yours (\"Owner\", for a creator or owner) or shared with you (\"Shared\"). */\n@Component({\n selector: \"workspace-role-tag\",\n imports: [TranslocoPipe, UserIcon, UserRoundIcon],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"inline-flex items-center gap-1 rounded bg-(--bg-indigo-base-alt) px-1 py-0.5 text-xs text-(--font-indigo-base)\",\n \"[attr.data-owner]\": \"owned() ? '' : null\"\n },\n template: `\n @if (owned()) {\n <user-icon class=\"size-3\" />\n {{ \"documentWorkspaces.list.roleOwner\" | transloco }}\n } @else {\n <user-round-icon class=\"size-3\" />\n {{ \"documentWorkspaces.list.roleShared\" | transloco }}\n }\n `\n})\nexport class WorkspaceRoleTag {\n readonly role = input.required<WorkspaceRole>();\n\n protected readonly owned = computed(() => canWrite(this.role()));\n}\n","import { ApiError } from \"@sinequa/atomic\";\n\n/**\n * Either a translation key (under the `documentWorkspaces` scope) or text to\n * show verbatim. The split keeps this module free of any translation engine:\n * the view passes `key` to the `transloco` pipe and prints `text` as is.\n */\nexport type WorkspaceErrorMessage = { key: string } | { text: string };\n\n/**\n * Maps a caught error to the message shown in an inline error banner.\n *\n * Only the statuses we recognise get a catalog entry. The default branch\n * deliberately passes the server's own prose through untranslated (a 409\n * `container_locked` has no dedicated error class and its message is already\n * human-readable): localizing the rest would need the API to return stable\n * error codes instead of English sentences.\n *\n * @param err - The caught value, of any type.\n */\nexport function workspaceErrorMessage(err: unknown): WorkspaceErrorMessage {\n if (err instanceof ApiError) {\n switch (err.status) {\n case 401:\n return { key: \"documentWorkspaces.errors.signIn\" };\n case 403:\n return { key: \"documentWorkspaces.errors.forbidden\" };\n case 404:\n return { key: \"documentWorkspaces.errors.notFound\" };\n default:\n return err.message ? { text: err.message } : { key: \"documentWorkspaces.errors.generic\" };\n }\n }\n return err instanceof Error && err.message ? { text: err.message } : { key: \"documentWorkspaces.errors.generic\" };\n}\n","import { afterNextRender, ChangeDetectionStrategy, Component, computed, signal, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n createCallable,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogResult,\n DialogTitleComponent,\n injectCallRef\n} from \"@sinequa/galactik\";\nimport { workspaceErrorMessage, WorkspaceErrorMessage } from \"../../utils/error-message\";\nimport { splitByMatch, TextChunk } from \"../../utils/text-match\";\n\nexport interface ConfirmDialogConfig {\n /** Already translated. */\n title: string;\n /** Already translated, with the subject interpolated. */\n description?: string;\n /** A substring of `description` to emphasise — typically the name of what is about to be deleted. */\n emphasis?: string;\n /** Already translated. Defaults to \"Delete\" when `destructive`, \"Save\" otherwise. */\n confirmLabel?: string;\n /** Already translated; shown on the confirm button while `action` runs. Defaults to \"Deleting…\" when `destructive`. */\n busyLabel?: string;\n /** Already translated. Defaults to \"Cancel\". */\n cancelLabel?: string;\n /** Styles the confirm button as a destructive action. */\n destructive?: boolean;\n /**\n * What confirming does. While it runs the dialog cannot be dismissed (Cancel,\n * Escape), so a request cannot be abandoned half-way through; if it rejects,\n * the dialog stays open with the error so it can be retried. Without it,\n * confirming just closes the dialog with `dialog-confirm`.\n */\n action?: () => Promise<unknown>;\n}\n\n/** Text to print as is, or a key of the `documentWorkspaces` scope to translate. */\ntype Label = { key: string } | { text: string };\n\n/**\n * The one confirmation dialog — \"delete these workspaces?\", \"delete this\n * document?\" — instead of a modal per use.\n *\n * ```ts\n * const result = await ConfirmAction.call({\n * title, description, emphasis: name, destructive: true,\n * action: () => store.remove([id])\n * });\n * if (result === \"dialog-confirm\") …\n * ```\n *\n * Resolves `dialog-confirm` once the action succeeded, `dialog-cancel` otherwise.\n */\n@Component({\n selector: \"workspace-confirm-dialog\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogTitleComponent\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <dialog #dialog (cancel)=\"onNativeCancel($event)\" (closed)=\"call.end($event)\">\n <DialogContent size=\"md\">\n <!-- The header's close button cannot be disabled, so the whole header goes inert while the action runs. -->\n <DialogHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\" [attr.inert]=\"busy() ? '' : null\">\n <DialogTitle>{{ config().title }}</DialogTitle>\n </DialogHeader>\n\n @if (config().description || error()) {\n <DialogBody>\n @if (config().description) {\n <p class=\"text-sm\">\n @for (chunk of descriptionChunks(); track $index) {\n @if (chunk.match) {\n <strong>{{ chunk.text }}</strong>\n } @else {\n {{ chunk.text }}\n }\n }\n </p>\n }\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">\n @if (\"key\" in message) {\n {{ message.key | transloco }}\n } @else {\n {{ message.text }}\n }\n </p>\n }\n </DialogBody>\n }\n\n <DialogFooter>\n <button variant=\"secondary\" [disabled]=\"busy()\" (click)=\"dialog.close('dialog-cancel')\">\n @let cancel = cancelLabel();\n @if (\"key\" in cancel) {\n {{ cancel.key | transloco }}\n } @else {\n {{ cancel.text }}\n }\n </button>\n <button\n variant=\"primary\"\n [class]=\"config().destructive ? 'bg-(--bg-error-base) enabled:hover:bg-(--bg-error-base) enabled:hover:opacity-90' : ''\"\n [disabled]=\"busy()\"\n (click)=\"confirm()\">\n @let confirmation = confirmLabel();\n @if (\"key\" in confirmation) {\n {{ confirmation.key | transloco }}\n } @else {\n {{ confirmation.text }}\n }\n </button>\n </DialogFooter>\n </DialogContent>\n </dialog>\n `\n})\nexport class ConfirmDialog {\n protected readonly call = injectCallRef<ConfirmDialogConfig, DialogResult>();\n\n private readonly dialog = viewChild.required(DialogComponent);\n\n protected readonly config = computed(() => this.call.props());\n protected readonly busy = signal(false);\n protected readonly error = signal<WorkspaceErrorMessage | null>(null);\n\n protected readonly descriptionChunks = computed<TextChunk[]>(() => {\n const { description = \"\", emphasis } = this.config();\n // Only the first occurrence is emphasised: the subject appears once in the sentence.\n let emphasised = false;\n return splitByMatch(description, emphasis ?? \"\").map(chunk => {\n if (!chunk.match || emphasised) return { ...chunk, match: false };\n emphasised = true;\n return chunk;\n });\n });\n\n protected readonly cancelLabel = computed<Label>(() => {\n const { cancelLabel } = this.config();\n return cancelLabel ? { text: cancelLabel } : { key: \"documentWorkspaces.common.cancel\" };\n });\n\n protected readonly confirmLabel = computed<Label>(() => {\n const { confirmLabel, busyLabel, destructive } = this.config();\n if (this.busy() && busyLabel) return { text: busyLabel };\n if (this.busy() && destructive && !confirmLabel) return { key: \"documentWorkspaces.common.deleting\" };\n if (confirmLabel) return { text: confirmLabel };\n return { key: destructive ? \"documentWorkspaces.common.delete\" : \"documentWorkspaces.common.save\" };\n });\n\n constructor() {\n afterNextRender(() => this.dialog().showModal());\n }\n\n protected async confirm(): Promise<void> {\n const { action } = this.config();\n if (!action) {\n this.dialog().close(\"dialog-confirm\");\n return;\n }\n\n this.busy.set(true);\n this.error.set(null);\n try {\n await action();\n } catch (err) {\n this.error.set(workspaceErrorMessage(err));\n return;\n } finally {\n this.busy.set(false);\n }\n this.dialog().close(\"dialog-confirm\");\n }\n\n /** Escape on a native <dialog> closes it: refuse while the action is running. */\n protected onNativeCancel(event: Event): void {\n if (this.busy()) event.preventDefault();\n }\n}\n\n/** Imperative entry point: `await ConfirmAction.call({ title, destructive: true, action })`. */\nexport const ConfirmAction = createCallable<ConfirmDialogConfig, DialogResult>(ConfirmDialog);\n","import type { TrackedUpload } from \"../models/upload.model\";\n\n/**\n * Text to print as is, or a key of the `documentWorkspaces` scope (with its\n * parameters) to translate. Lets a class decide *what* to say and leave *saying\n * it* to the template, which has the translation pipe.\n */\nexport type Message = { text: string } | { key: string; params?: Record<string, unknown> };\n\n/** A message from the `tracker` namespace. */\nexport function trackerMessage(key: string, params?: Record<string, unknown>): Message {\n return { key: `documentWorkspaces.tracker.${key}`, params };\n}\n\n/** Shortens a file name to `max` characters, keeping its extension: `long-na….pdf`. */\nexport function truncateName(name: string, max: number): string {\n if (name.length <= max) return name;\n const dot = name.lastIndexOf(\".\");\n if (dot > 0 && name.length - dot <= 5) {\n const extension = name.slice(dot);\n return `${name.slice(0, max - extension.length - 1)}…${extension}`;\n }\n return `${name.slice(0, max - 1)}…`;\n}\n\n/**\n * What to call a tracked row: the file's name — or, for a job started elsewhere whose files\n * the status API does not list, whose job it is.\n */\nexport function uploadLabel(upload: Pick<TrackedUpload, \"name\" | \"remote\">, max: number): Message {\n if (upload.name) return { text: truncateName(upload.name, max) };\n return trackerMessage(upload.remote?.mine ? \"ownUpload\" : \"remoteUpload\");\n}\n\n/** `m:ss` for a duration in ms, clamped at 0 so a late tick cannot go negative. */\nexport function formatClock(ms: number): string {\n const seconds = Math.max(0, Math.round(ms / 1000));\n return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, \"0\")}`;\n}\n","import { ChangeDetectionStrategy, Component, input } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport type { Message } from \"../../utils/message\";\n\n/** Prints a {@link Message}: its text as is, or its key translated. */\n@Component({\n selector: \"workspace-message\",\n imports: [TranslocoPipe],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"contents\" },\n template: `\n @let m = message();\n @if (\"key\" in m) {\n {{ m.key | transloco: m.params }}\n } @else {\n {{ m.text }}\n }\n `\n})\nexport class WorkspaceMessage {\n readonly message = input.required<Message>();\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport type { DocumentStatus as Status } from \"../../models/workspace.model\";\nimport { Message, trackerMessage } from \"../../utils/message\";\nimport { WorkspaceMessage } from \"./workspace-message\";\n\n/**\n * The badge of a document that is not finished: \"1/2 Uploading\", \"2/2 Indexing\"\n * (with animated dots while it moves) or \"Upload failed\". Renders nothing for a\n * document that is indexed, or whose state is not known: the card shows its date.\n */\n@Component({\n selector: \"document-status-badge\",\n imports: [WorkspaceMessage],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"inline-flex items-center gap-1 text-xs data-[status=error]:text-(--font-error-base)\",\n \"[hidden]\": \"!message()\",\n \"[attr.data-status]\": \"status() ?? null\"\n },\n template: `\n @if (message(); as message) {\n <workspace-message [message]=\"message\" />\n @if (status() !== \"error\") {\n <span aria-hidden=\"true\" class=\"flex gap-0.5 motion-safe:animate-pulse\"><i class=\"bg-current size-1 rounded-full\"></i><i class=\"bg-current size-1 rounded-full\"></i><i class=\"bg-current size-1 rounded-full\"></i></span>\n }\n }\n `\n})\nexport class DocumentStatusBadge {\n /** `null` or absent once the document is indexed. */\n readonly status = input<Status | null | undefined>();\n\n protected readonly message = computed<Message | null>(() => {\n switch (this.status()) {\n case \"uploading\":\n return trackerMessage(\"stageUploading\");\n case \"indexing\":\n return trackerMessage(\"stageIndexing\");\n case \"error\":\n return trackerMessage(\"uploadFailed\");\n default:\n return null;\n }\n });\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { FileIcon, FileSpreadsheetIcon, FileTextIcon } from \"@sinequa/galactik\";\nimport type { DocumentType } from \"../../models/workspace.model\";\n\n// Full class names: Tailwind only generates the ones it can read as literals.\nconst TYPE_CLASSES: Record<DocumentType, string> = {\n pdf: \"bg-(--red-100,#FFDEDF) text-(--red-600,#AD3739)\",\n csv: \"bg-(--green-100,#E5EEE3) text-(--green-600,#3E6936)\",\n doc: \"bg-(--blue-100,#E2EDF4) text-(--blue-600,#45677E)\"\n};\n\n/** The icon of a document, on a tile tinted by the family `getDocumentType()` puts its file name in. */\n@Component({\n selector: \"document-type-icon\",\n imports: [FileIcon, FileSpreadsheetIcon, FileTextIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"inline-flex shrink-0 items-center justify-center rounded-lg\",\n \"[class]\": \"classes()\",\n \"[attr.data-type]\": \"type()\"\n },\n template: `\n @switch (type()) {\n @case (\"csv\") {\n <file-spreadsheet-icon class=\"size-5 opacity-80\" />\n }\n @case (\"pdf\") {\n <file-text-icon class=\"size-5 opacity-80\" />\n }\n @default {\n <file-icon class=\"size-5 opacity-80\" />\n }\n }\n `\n})\nexport class DocumentTypeIcon {\n readonly type = input.required<DocumentType>();\n /** `lg` (36px) on a card, `md` (32px) in a row. */\n readonly size = input<\"md\" | \"lg\">(\"lg\");\n\n protected readonly classes = computed(() => `${TYPE_CLASSES[this.type()]} ${this.size() === \"lg\" ? \"size-9\" : \"size-8\"}`);\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { FolderIcon } from \"@sinequa/galactik\";\nimport type { WorkspaceColor } from \"../../models/workspace.model\";\n\n// Full class names, never built from the colour: Tailwind only generates the\n// classes it can read as literals. A tint of the ramp for the tile, a stronger one\n// for the folder; the hex is the fallback for a theme that lacks the ramp.\nconst COLOR_CLASSES: Record<WorkspaceColor, string> = {\n default: \"bg-(--yellow-100,#FDF2DC) text-(--yellow-400,#F6CA72)\",\n teal: \"bg-(--teal-100,#E0F4F1) text-(--teal-400,#4EBFB0)\",\n pink: \"bg-(--pink-100,#FAECFB) text-(--pink-400,#C9AACB)\",\n blue: \"bg-(--blue-100,#E2EDF4) text-(--blue-400,#8CB8D5)\",\n orange: \"bg-(--orange-100,#FFEEE2) text-(--orange-400,#FFBA8D)\",\n indigo: \"bg-(--indigo-100,#E5E9F9) text-(--indigo-400,#98A9E5)\",\n green: \"bg-(--green-100,#E5EEE3) text-(--green-400,#709B67)\"\n};\n\n/** A workspace's folder on a tile tinted with the accent derived from its id (see `workspaceColorFor()`). */\n@Component({\n selector: \"workspace-icon\",\n imports: [FolderIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"inline-flex shrink-0 items-center justify-center rounded-(--radius-md)\",\n \"[class]\": \"classes()\",\n \"[attr.data-color]\": \"color()\"\n },\n template: `<folder-icon class=\"size-5 opacity-80\" />`\n})\nexport class WorkspaceIcon {\n readonly color = input<WorkspaceColor>(\"default\");\n /** `lg` (38px) on a card, `md` (32px) in a row. */\n readonly size = input<\"md\" | \"lg\">(\"lg\");\n\n protected readonly classes = computed(() => `${COLOR_CLASSES[this.color()]} ${this.size() === \"lg\" ? \"size-[38px]\" : \"size-8\"}`);\n}\n","import { ChangeDetectionStrategy, Component, input } from \"@angular/core\";\nimport { getDocumentType } from \"../../utils/doc-type\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\n\n/** The files staged for upload, before anything is sent. */\n@Component({\n selector: \"upload-file-list\",\n imports: [DocumentTypeIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { role: \"list\", class: \"flex max-h-48 flex-col gap-1 overflow-y-auto\" },\n template: `\n @for (file of files(); track file.name) {\n <div role=\"listitem\" class=\"flex items-center gap-2 text-sm\">\n <document-type-icon [type]=\"typeOf(file)\" />\n <!-- Rendered through Angular's escaping: a file name is user-controlled. -->\n <span class=\"truncate\" [attr.title]=\"file.name\">{{ file.name }}</span>\n </div>\n }\n `\n})\nexport class UploadFileList {\n readonly files = input.required<readonly File[]>();\n\n protected typeOf(file: File) {\n return getDocumentType(file.name);\n }\n}\n","import { afterNextRender, ChangeDetectionStrategy, Component, computed, inject, signal, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n createCallable,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogResult,\n DialogTitleComponent,\n injectCallRef\n} from \"@sinequa/galactik\";\nimport { Dropzone } from \"../../directives/dropzone.directive\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { UploadService } from \"../../upload/upload.service\";\nimport { canWrite } from \"../../utils/workspace-role\";\nimport { UploadFileList } from \"./upload-file-list\";\n\nexport interface UploadDialogConfig {\n /** The workspace the files go to. */\n workspace: Pick<Workspace, \"id\" | \"name\" | \"currentUserRole\">;\n /** The tus endpoint — the host's `upload-endpoint`. */\n endpoint: string;\n}\n\n/**\n * \"Upload Documents\": stage files by dropping or picking them, then send them all\n * as one batch.\n *\n * Staging only — nothing leaves the browser until Upload is pressed, so the whole\n * selection goes out as ONE ingest job instead of each drop starting its own.\n * Dismissing the dialog any other way discards the selection: nothing was sent,\n * so there is nothing to keep.\n *\n * ```ts\n * await UploadDocuments.call({ workspace, endpoint });\n * ```\n *\n * Refuses to open for someone who cannot write to the workspace: the Upload\n * button is hidden for them, this is the belt to that braces.\n */\n@Component({\n selector: \"upload-dialog\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n DialogComponent,\n DialogBodyComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogTitleComponent,\n Dropzone,\n UploadFileList\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <dialog #dialog (closed)=\"call.end($event)\">\n <DialogContent size=\"md\">\n <DialogHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\">\n <DialogTitle>{{ \"documentWorkspaces.upload.modalTitle\" | transloco }}</DialogTitle>\n </DialogHeader>\n\n <DialogBody>\n <div\n dropzone\n (filesSelected)=\"stage($event)\"\n [attr.aria-label]=\"'documentWorkspaces.upload.dropzoneLabel' | transloco\"\n class=\"border-(--stroke-primary-lighter) hover:border-(--stroke-primary-base) data-dragover:border-(--stroke-primary-base) data-dragover:bg-(--bg-primary-lightest) focus-visible:ring-(--stroke-primary-base) cursor-pointer rounded-xl border-2 border-dashed p-8 text-center text-sm transition-colors outline-none focus-visible:ring-2\">\n {{ \"documentWorkspaces.upload.dropPrefix\" | transloco }}\n <span class=\"text-(--font-primary-base) underline\">{{ \"documentWorkspaces.upload.browse\" | transloco }}</span>\n </div>\n\n @if (files().length) {\n <upload-file-list [files]=\"files()\" />\n }\n </DialogBody>\n\n <DialogFooter>\n <button variant=\"secondary\" (click)=\"dialog.close('dialog-cancel')\">\n {{ \"documentWorkspaces.common.cancel\" | transloco }}\n </button>\n <button [disabled]=\"!files().length\" (click)=\"upload()\">\n @if (files().length) {\n {{ \"documentWorkspaces.upload.uploadCount\" | transloco: { count: files().length } }}\n } @else {\n {{ \"documentWorkspaces.detail.upload\" | transloco }}\n }\n </button>\n </DialogFooter>\n </DialogContent>\n </dialog>\n `\n})\nexport class UploadDialog {\n protected readonly call = injectCallRef<UploadDialogConfig, DialogResult>();\n\n private readonly dialog = viewChild.required(DialogComponent);\n private readonly uploads = inject(UploadService);\n\n protected readonly files = signal<File[]>([]);\n\n /** Whether there is anything to send. */\n protected readonly ready = computed(() => this.files().length > 0);\n\n constructor() {\n afterNextRender(() => {\n if (!canWrite(this.call.props().workspace.currentUserRole)) {\n this.call.end(\"dialog-cancel\");\n return;\n }\n this.dialog().showModal();\n });\n }\n\n /**\n * Adds files to the staged selection. No gatekeeping here either: no type, count\n * or size check. Same-name files are still collapsed, and for a hard reason: a\n * document is addressed as `<jobId>/<fileName>`, so two files sharing a name\n * inside one job would collide on the server, not just in the list.\n */\n protected stage(files: File[]): void {\n this.files.update(staged => {\n const names = new Set(staged.map(f => f.name));\n const added = files.filter(f => !names.has(f.name) && names.add(f.name));\n return added.length ? [...staged, ...added] : staged;\n });\n }\n\n protected upload(): void {\n if (!this.ready()) return;\n const { workspace, endpoint } = this.call.props();\n this.uploads.addFiles(workspace, this.files(), { endpoint });\n this.dialog().close(\"dialog-confirm\");\n }\n}\n\n/** Imperative entry point: `await UploadDocuments.call({ workspace, endpoint })`. */\nexport const UploadDocuments = createCallable<UploadDialogConfig, DialogResult>(UploadDialog);\n","import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input, output, signal } from \"@angular/core\";\nimport type { ConnectionNotice } from \"../../models/upload.model\";\nimport { formatClock, Message, trackerMessage } from \"../../utils/message\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\nimport { ButtonComponent } from \"@sinequa/galactik\";\n\n/**\n * What the tracker says when the connection or the session is in trouble: what\n * happened, what is being done about it, and a countdown.\n *\n * It exists only while there is something to say, so its one-second clock only\n * runs then.\n */\n@Component({\n selector: \"connection-banner\",\n imports: [WorkspaceMessage, ButtonComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { role: \"status\", class: \"flex flex-col gap-1 border-b border-(--stroke-primary-lighter) bg-(--bg-error-lightest) px-4 py-3 text-sm\" },\n template: `\n <div class=\"font-medium\"><workspace-message [message]=\"title()\" /></div>\n <div class=\"text-(--font-primary-muted)\"><workspace-message [message]=\"detail()\" /></div>\n <div class=\"text-(--font-primary-muted) text-xs\" data-testid=\"countdown\">\n @for (part of countdown(); track $index) {\n @if ($index > 0) {\n <span aria-hidden=\"true\"> · </span>\n }\n <workspace-message [message]=\"part\" />\n }\n </div>\n @if (showRetry()) {\n <button variant=\"secondary\" size=\"sm\" class=\"self-start\" (click)=\"retry.emit()\">\n <workspace-message [message]=\"retryLabel\" />\n </button>\n }\n `\n})\nexport class ConnectionBanner {\n readonly notice = input.required<ConnectionNotice>();\n /** The Retry button was pressed — offered once signed out, when the user can act. */\n readonly retry = output<void>();\n\n protected readonly retryLabel: Message = { key: \"documentWorkspaces.common.retry\" };\n\n private readonly now = signal(Date.now());\n\n constructor() {\n const timer = setInterval(() => this.now.set(Date.now()), 1000);\n inject(DestroyRef).onDestroy(() => clearInterval(timer));\n }\n\n protected readonly title = computed<Message>(() => {\n switch (this.notice().phase) {\n case \"reconnecting\":\n return trackerMessage(\"reconnecting\");\n case \"signed-out\":\n return trackerMessage(\"signedOut\");\n default:\n return trackerMessage(\"connectionLost\");\n }\n });\n\n protected readonly detail = computed<Message>(() => {\n const { phase, uploads, attempt, maxAttempts } = this.notice();\n if (phase === \"reconnecting\") return trackerMessage(\"restoringSession\");\n if (phase === \"signed-out\") return uploads ? trackerMessage(\"uploadsPaused\", { count: uploads }) : trackerMessage(\"signInAgain\");\n return uploads\n ? trackerMessage(\"retryAttemptWithFiles\", { attempt, max: maxAttempts, count: uploads })\n : trackerMessage(\"retryAttempt\", { attempt, max: maxAttempts });\n });\n\n /** \"Next try in 0:03 · lost 0:12 ago · gives up in 4:40\", the parts that apply. */\n protected readonly countdown = computed<Message[]>(() => {\n const { phase, retryAtMs, budgetEndsAtMs, sinceMs } = this.notice();\n const now = this.now();\n const parts: Message[] = [];\n if (phase === \"retrying\" && retryAtMs !== undefined) {\n const left = retryAtMs - now;\n parts.push(left > 0 ? trackerMessage(\"nextTryIn\", { time: formatClock(left) }) : trackerMessage(\"tryingNow\"));\n }\n parts.push(trackerMessage(\"lostAgo\", { time: formatClock(now - sinceMs) }));\n if (phase === \"retrying\" && budgetEndsAtMs !== undefined) parts.push(trackerMessage(\"givesUpIn\", { time: formatClock(budgetEndsAtMs - now) }));\n return parts;\n });\n\n protected readonly showRetry = computed(() => this.notice().phase === \"signed-out\" && !!this.notice().retry);\n}\n","import { ChangeDetectionStrategy, Component, ViewEncapsulation } from \"@angular/core\";\n\n/**\n * Three dots that light up one after the other: something is going on.\n *\n * The keyframes ship with the component (a library has no stylesheet of its own to\n * put them in); encapsulation is turned off for them to reach the dots, and their\n * names carry the component's so they cannot collide with the host page's.\n */\n@Component({\n selector: \"activity-dots\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: { class: \"inline-flex gap-px\", \"aria-hidden\": \"true\" },\n styles: `\n @keyframes activity-dots-appear {\n 0%,\n 60%,\n 100% {\n opacity: 0;\n }\n 30% {\n opacity: 1;\n }\n }\n activity-dots > span {\n display: inline-block;\n opacity: 0;\n animation: activity-dots-appear 1.4s infinite;\n }\n activity-dots > span:nth-child(2) {\n animation-delay: 0.2s;\n }\n activity-dots > span:nth-child(3) {\n animation-delay: 0.4s;\n }\n @media (prefers-reduced-motion: reduce) {\n activity-dots > span {\n animation: none;\n opacity: 1;\n }\n }\n `,\n template: `<span>.</span><span>.</span><span>.</span>`\n})\nexport class ActivityDots {}\n\n/** A ring that turns: something is going on. */\n@Component({\n selector: \"activity-spinner\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: { class: \"inline-block shrink-0 rounded-full border-2 border-(--bg-primary-light) border-t-(--stroke-secondary-base)\", \"aria-hidden\": \"true\" },\n styles: `\n @keyframes activity-spinner-turn {\n to {\n transform: rotate(360deg);\n }\n }\n activity-spinner {\n animation: activity-spinner-turn 0.85s linear infinite;\n }\n @media (prefers-reduced-motion: reduce) {\n activity-spinner {\n animation-duration: 3s;\n }\n }\n `,\n template: ``\n})\nexport class ActivitySpinner {}\n","import { ChangeDetectionStrategy, Component, computed, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckIcon, ChevronDownIcon, CircleXIcon, RefreshIcon, SyncIcon, UploadIcon, XMarkIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport type { TrackedUpload, UploadStatus } from \"../../models/upload.model\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\nimport { ActivityDots } from \"../shared/activity-indicators\";\n\ninterface SectionConfig {\n titleKey: string;\n /** Shows the average progress as a bar, and animated dots next to the title. */\n progress: boolean;\n /** Can be folded away — only the two sections that move. */\n foldable: boolean;\n}\n\n// Keys are spelled out, never built from the status: they are read by tooling.\nconst SECTIONS: Record<UploadStatus, SectionConfig> = {\n uploading: { titleKey: \"documentWorkspaces.tracker.sectionUploading\", progress: true, foldable: true },\n indexing: { titleKey: \"documentWorkspaces.tracker.sectionIndexing\", progress: true, foldable: true },\n done: { titleKey: \"documentWorkspaces.tracker.sectionCompleted\", progress: false, foldable: false },\n error: { titleKey: \"documentWorkspaces.tracker.sectionFailed\", progress: false, foldable: false },\n rejected: { titleKey: \"documentWorkspaces.tracker.sectionNotIndexed\", progress: false, foldable: false }\n};\n\n/**\n * The files of one status, for one workspace, in the tracker's expanded body:\n * a title, an average progress bar while they move, and a row per file with what\n * can be done to it.\n *\n * The files fold away and open again, and the bar follows the progress, with a\n * transition rather than a jump.\n *\n * It only reports what the user asked for; `UploadTracker` does it.\n */\n@Component({\n selector: \"upload-tracker-section\",\n imports: [\n TranslocoPipe,\n ActivityDots,\n ButtonComponent,\n CheckIcon,\n ChevronDownIcon,\n CircleXIcon,\n DocumentTypeIcon,\n RefreshIcon,\n SyncIcon,\n TooltipDirective,\n UploadIcon,\n XMarkIcon\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"block px-4 py-3 data-[status=error]:bg-(--bg-error-lightest) data-[status=rejected]:bg-(--bg-error-lightest)\",\n \"[attr.data-status]\": \"status()\"\n },\n template: `\n <div class=\"flex items-center gap-2\">\n <span class=\"flex shrink-0 items-center text-(--font-neutral-muted)\" [class.text-(--font-success-base)]=\"status() === 'done'\" [class.text-(--font-error-base)]=\"failed()\">\n @switch (status()) {\n @case (\"uploading\") {\n <upload-icon class=\"size-4\" />\n }\n @case (\"indexing\") {\n <sync-icon class=\"size-4\" />\n }\n @case (\"done\") {\n <check-icon class=\"size-4\" />\n }\n @default {\n <circle-x-icon class=\"size-4\" />\n }\n }\n </span>\n <span class=\"inline-flex shrink-0 items-baseline text-xs text-(--font-neutral-black)\" [class.text-(--font-error-base)]=\"failed()\">\n {{ config().titleKey | transloco }}\n @if (config().progress) {\n <activity-dots />\n }\n </span>\n @if (config().progress) {\n <div\n role=\"progressbar\"\n [attr.aria-valuenow]=\"average()\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n class=\"h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-(--bg-primary-lighter)\">\n <div class=\"h-full rounded-full bg-(--bg-primary-base) transition-[width] duration-[400ms] ease-[ease] motion-reduce:transition-none\" [style.width.%]=\"average()\"></div>\n </div>\n } @else {\n <span class=\"flex-1\"></span>\n }\n <span\n class=\"rounded-full bg-(--bg-neutral-base) px-2 text-xs text-(--font-neutral-base)\"\n [class.bg-(--bg-success-base-alt)]=\"status() === 'done'\"\n [class.text-(--font-success-base)]=\"status() === 'done'\"\n [class.bg-(--bg-error-base-alt)]=\"failed()\"\n [class.text-(--font-error-base)]=\"failed()\">\n {{ uploads().length }}\n </span>\n @if (config().foldable) {\n <button\n class=\"shrink-0\"\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-label]=\"'documentWorkspaces.tracker.toggleSection' | transloco\"\n [tooltip]=\"(open() ? 'documentWorkspaces.common.collapse' : 'documentWorkspaces.common.expand') | transloco\"\n (click)=\"toggle.emit()\">\n <chevron-down-icon class=\"transition-transform duration-[250ms] ease-[ease]\" [class.-rotate-180]=\"open()\" />\n </button>\n }\n </div>\n\n <!-- Folding keeps the rows in place and squeezes them away, which is what lets it be seen. -->\n <div\n class=\"max-h-0 overflow-hidden pt-0 opacity-0 transition-[max-height,opacity,padding] duration-300 ease-[ease] motion-reduce:transition-none data-open:max-h-[600px] data-open:pt-2 data-open:opacity-100\"\n [attr.data-open]=\"open() || !config().foldable ? '' : null\"\n [attr.inert]=\"open() || !config().foldable ? null : ''\">\n <ul class=\"flex flex-col\">\n @for (upload of uploads(); track upload.id) {\n <li class=\"flex items-center gap-3 py-2 not-first:border-t not-first:border-(--stroke-primary-lighter)\" [attr.data-status]=\"upload.status\">\n <document-type-icon class=\"shrink-0\" [type]=\"upload.type\" />\n <!-- Text, never markup: a file name and a server message are user-controlled. -->\n <span\n class=\"min-w-0 flex-1 truncate text-xs text-(--font-neutral-black)\"\n [class.text-(--font-error-base)]=\"upload.status === 'error' || upload.status === 'rejected'\"\n [attr.title]=\"upload.name || (label(upload) | transloco)\"\n >{{ upload.name || (label(upload) | transloco) }}</span\n >\n @if (upload.message) {\n <span class=\"max-w-40 shrink truncate text-[11px] text-(--font-neutral-muted)\" [attr.title]=\"upload.message\">{{ upload.message }}</span>\n }\n\n @switch (upload.status) {\n @case (\"uploading\") {\n <span class=\"shrink-0 text-xs text-(--font-neutral-muted) tabular-nums\">{{ round(upload.percent) }}%</span>\n <!-- A transfer this client does not hold cannot be stopped from here, and nor can it be closed while it goes on. -->\n @if (!upload.remote) {\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.cancelUpload' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.cancelUpload' | transloco\" (click)=\"discard.emit(upload.id)\">\n <x-mark-icon />\n </button>\n }\n }\n @case (\"indexing\") {\n <!-- No percentage here: the server only reports one per job, shown by the bar above. -->\n <activity-dots class=\"shrink-0 text-(--font-neutral-muted)\" />\n }\n @case (\"done\") {\n <span class=\"shrink-0 text-xs whitespace-nowrap text-(--font-success-base)\">{{ \"documentWorkspaces.tracker.complete\" | transloco }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.dismiss' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.dismiss' | transloco\" (click)=\"discard.emit(upload.id)\">\n <x-mark-icon />\n </button>\n }\n @case (\"error\") {\n @if (upload.retryable) {\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.retryUpload' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.retryUpload' | transloco\" (click)=\"retry.emit(upload.id)\">\n <refresh-icon />\n </button>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.dismiss' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.dismiss' | transloco\" (click)=\"discard.emit(upload.id)\">\n <x-mark-icon />\n </button>\n }\n @case (\"rejected\") {\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.dismiss' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.dismiss' | transloco\" (click)=\"discard.emit(upload.id)\">\n <x-mark-icon />\n </button>\n }\n }\n </li>\n }\n </ul>\n </div>\n `\n})\nexport class UploadTrackerSection {\n readonly status = input.required<UploadStatus>();\n readonly uploads = input.required<readonly TrackedUpload[]>();\n readonly open = input(true);\n /** The phase's overall progress, when the tracker knows better than the mean of the files listed here (those that moved on are not). */\n readonly progress = input<number | undefined>(undefined);\n\n readonly toggle = output<void>();\n /** Cancel an upload in progress, or dismiss a finished row. The tracker tells the two apart. */\n readonly discard = output<string>();\n readonly retry = output<string>();\n\n protected readonly config = computed(() => SECTIONS[this.status()]);\n /** A failure, or a file the server will not index: both are in the error colours. */\n protected readonly failed = computed(() => this.status() === \"error\" || this.status() === \"rejected\");\n\n /** The bar: the phase's overall progress, or else the mean progress of the files in the section. */\n protected readonly average = computed(() => {\n const progress = this.progress();\n if (progress !== undefined) return progress;\n const uploads = this.uploads();\n return uploads.length ? Math.round(uploads.reduce((sum, u) => sum + u.percent, 0) / uploads.length) : 0;\n });\n\n /** The translation key standing for a row with no file name: a job started elsewhere. Only read when `name` is empty. */\n protected label(upload: TrackedUpload): string {\n return upload.remote?.mine ? \"documentWorkspaces.tracker.ownUpload\" : \"documentWorkspaces.tracker.remoteUpload\";\n }\n\n protected round(value: number): number {\n return Math.round(value);\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, DestroyRef, effect, inject, signal, untracked } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n CircleXIcon,\n SyncIcon,\n UploadIcon,\n XMarkIcon,\n TooltipDirective\n} from \"@sinequa/galactik\";\nimport type { TrackedUpload, UploadStatus } from \"../../models/upload.model\";\nimport { UploadTrackerStore, isFailed, isFinished } from \"../../upload/upload-tracker.store\";\nimport { UploadService } from \"../../upload/upload.service\";\nimport { Message, trackerMessage, truncateName, uploadLabel } from \"../../utils/message\";\nimport { FINISHED_UPLOAD_VISIBLE_MS } from \"../../utils/upload-jobs\";\nimport { ActivityDots, ActivitySpinner } from \"../shared/activity-indicators\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\nimport { ConnectionBanner } from \"./connection-banner\";\nimport { UploadTrackerSection } from \"./upload-tracker-section\";\n\n/**\n * - `inline`: a compact header and a line of counters;\n * - `expanded`: the files, section by section, workspace by workspace;\n * - `minimized`: slid down to the corner, only a strip showing; it still reports what happens, and slides back when hovered.\n */\ntype Mode = \"inline\" | \"expanded\" | \"minimized\";\n\ntype Tone = \"normal\" | \"flash\" | \"success\";\n\ninterface TrackedWorkspace {\n id: string;\n name: string;\n}\n\nconst SECTION_ORDER: readonly UploadStatus[] = [\"uploading\", \"indexing\", \"done\", \"error\", \"rejected\"];\n\n// How long what just happened stays on screen, in ms.\nconst FLASH_MS = 3000;\nconst PEEK_MS = 3500;\nconst WORKSPACE_COMPLETE_MS = 3500;\n// How long a panel whose uploads are all over stays: long enough to read the outcome, even of an\n// upload someone else started and the user was not watching.\nconst AUTO_DISMISS_MS = FINISHED_UPLOAD_VISIBLE_MS;\nconst DISMISS_ANIMATION_MS = 320;\nconst HOVER_LEAVE_MS = 250;\n// The text on the strip leaves, then the new one comes: half a transition each.\nconst PEEK_SWAP_MS = 150;\nconst PEEK_REFLOW_MS = 30;\n\n/**\n * The floating panel that follows files from the first byte sent to the end of\n * indexing: what is moving, what is done, what failed and why, and what can be\n * done about it (cancel a transfer, retry a failed one, dismiss a finished one).\n *\n * It only reads `UploadTrackerStore` — the state — and asks `UploadService` to\n * act. It shows itself when files are tracked and, once every file is indexed\n * without a failure, celebrates and dismisses itself.\n *\n * The moments that pass by themselves (a file just finished, a workspace just\n * completed, a connection just dropped) are noticed from changes in the store,\n * so they work the same whether `UploadService` or a demo page drives it.\n */\n@Component({\n selector: \"upload-tracker\",\n imports: [\n TranslocoPipe,\n ActivityDots,\n ActivitySpinner,\n ButtonComponent,\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n CircleXIcon,\n ConnectionBanner,\n SyncIcon,\n TooltipDirective,\n UploadIcon,\n UploadTrackerSection,\n WorkspaceMessage,\n XMarkIcon\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"contents\" },\n template: `\n @if (store.active()) {\n <section\n class=\"group/tracker fixed right-6 bottom-6 z-900 w-[360px] max-w-[calc(100vw-2rem)] overflow-hidden rounded-(--radius-lg) border border-(--stroke-primary-lighter) bg-(--bg-neutral-white) shadow-[0_4px_8px_rgba(0,0,0,0.06),0_16px_32px_rgba(0,0,0,0.1)] transition-transform duration-[400ms] ease-[cubic-bezier(0.34,1.56,0.64,1)] motion-reduce:transition-none data-[state=minimized]:translate-x-[140px] data-[state=minimized]:translate-y-[calc(1.5rem+100%-44px)] data-[state=minimized]:cursor-pointer data-[state=minimized-hover]:translate-x-0 data-[state=minimized-hover]:translate-y-0 data-[state=minimized-hover]:cursor-pointer data-[state=minimized-hover]:duration-300 data-[state=minimized-hover]:ease-out data-minimizing:duration-[180ms] data-minimizing:ease-in data-dismissing:pointer-events-none data-dismissing:translate-y-3 data-dismissing:scale-[0.96] data-dismissing:opacity-0 data-dismissing:transition-[opacity,transform] data-dismissing:duration-300 data-dismissing:ease-[ease]\"\n [attr.data-state]=\"state()\"\n [attr.data-mode]=\"mode()\"\n [attr.data-tone]=\"header().tone\"\n [attr.data-minimizing]=\"minimizing() ? '' : null\"\n [attr.data-dismissing]=\"dismissing() ? '' : null\"\n (mouseenter)=\"onPointerEnter()\"\n (mouseleave)=\"onPointerLeave()\"\n (click)=\"onPanelClick()\"\n (transitionend)=\"onTransitionEnd($event)\">\n <!-- What shows of the panel once it slid down to the corner: a strip that keeps saying what goes on. -->\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[2] flex h-11 items-center gap-2 rounded-t-(--radius-lg) bg-(--bg-primary-lightest) px-4 opacity-0 transition-opacity duration-200 group-data-[state=minimized]/tracker:pointer-events-auto group-data-[state=minimized]/tracker:opacity-100 data-[kind=errors]:bg-(--bg-error-lightest) data-[kind=done]:bg-(--bg-success-lightest)\"\n [attr.data-kind]=\"peekKind()\"\n data-testid=\"peek\">\n @switch (peekTone()) {\n @case (\"success\") {\n <check-icon class=\"size-4 shrink-0 text-(--font-success-base)\" />\n }\n @case (\"error\") {\n <circle-x-icon class=\"size-4 shrink-0 text-(--font-error-base)\" />\n }\n @default {\n <activity-spinner class=\"size-4\" />\n }\n }\n <span\n class=\"min-w-0 flex-1 truncate text-xs text-(--font-neutral-black) transition-[opacity,transform] duration-150 data-[swap=below]:translate-y-[5px] data-[swap=below]:opacity-0 data-[swap=hidden]:-translate-y-[5px] data-[swap=hidden]:opacity-0 data-[tone=error]:font-medium data-[tone=error]:text-(--font-error-base) data-[tone=success]:font-medium data-[tone=success]:text-(--font-success-base)\"\n [attr.data-swap]=\"peekSwap()\"\n [attr.data-tone]=\"peekTone()\"\n aria-live=\"polite\"\n data-testid=\"peek-text\">\n <workspace-message [message]=\"peekShown()\" />\n </span>\n <span class=\"shrink-0 translate-x-1.5 text-xs text-(--font-neutral-muted) opacity-0 transition-[opacity,transform] delay-100 duration-200 group-hover/tracker:translate-x-0 group-hover/tracker:opacity-100\">\n {{ \"documentWorkspaces.tracker.clickToRestore\" | transloco }}\n </span>\n </div>\n\n <header\n class=\"flex items-center gap-3 border-b border-(--stroke-primary-lighter) bg-(--bg-primary-lightest) px-4 py-3 data-[tone=success]:bg-(--bg-success-lightest)\"\n [attr.data-tone]=\"header().tone === 'success' ? 'success' : null\">\n @if (header().tone === \"success\") {\n <check-icon class=\"size-5 shrink-0 text-(--font-success-base)\" />\n } @else {\n <activity-spinner class=\"size-5\" />\n }\n <div class=\"flex min-w-0 flex-1 flex-col gap-px\">\n <span class=\"truncate text-sm font-semibold text-(--font-neutral-black) group-data-[tone=success]/tracker:text-(--font-success-base)\" data-testid=\"name\">\n <workspace-message [message]=\"header().title\" />\n </span>\n <span\n class=\"truncate text-xs text-(--font-neutral-muted) transition-colors duration-200 data-[tone=flash]:font-medium data-[tone=flash]:text-(--font-success-base) data-[tone=success]:font-medium data-[tone=success]:text-(--font-success-base)\"\n [attr.data-tone]=\"header().tone\"\n aria-live=\"polite\"\n data-testid=\"count\">\n <workspace-message [message]=\"header().subtitle\" />\n </span>\n </div>\n <div class=\"relative flex shrink-0 items-center gap-1\">\n <span class=\"pointer-events-none absolute right-0 text-xs whitespace-nowrap text-(--font-primary-base) opacity-0 transition-opacity duration-200 group-data-[state=minimized-hover]/tracker:opacity-100\">\n {{ \"documentWorkspaces.tracker.clickToOpen\" | transloco }}\n </span>\n @if (canRetryAll()) {\n <button\n variant=\"secondary\"\n size=\"sm\"\n class=\"transition-opacity duration-150 group-data-[state=minimized-hover]/tracker:pointer-events-none group-data-[state=minimized-hover]/tracker:opacity-0\"\n (click)=\"retryAll(); $event.stopPropagation()\">{{ \"documentWorkspaces.tracker.retryAll\" | transloco }}</button>\n }\n @if (!store.allDone()) {\n <button\n class=\"transition-opacity duration-150 group-data-[state=minimized-hover]/tracker:pointer-events-none group-data-[state=minimized-hover]/tracker:opacity-0\"\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [attr.aria-expanded]=\"mode() === 'expanded'\"\n [attr.aria-label]=\"(mode() === 'expanded' ? 'documentWorkspaces.common.collapse' : 'documentWorkspaces.common.expand') | transloco\"\n [tooltip]=\"(mode() === 'expanded' ? 'documentWorkspaces.common.collapse' : 'documentWorkspaces.common.expand') | transloco\"\n (click)=\"toggleExpanded(); $event.stopPropagation()\">\n <chevron-down-icon class=\"transition-transform duration-[250ms]\" [class.rotate-180]=\"mode() !== 'expanded'\" />\n </button>\n }\n <button\n class=\"transition-opacity duration-150 group-data-[state=minimized-hover]/tracker:pointer-events-none group-data-[state=minimized-hover]/tracker:opacity-0\"\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"(mode() === 'minimized' || store.settled() ? 'documentWorkspaces.common.close' : 'documentWorkspaces.common.minimize') | transloco\"\n [tooltip]=\"(mode() === 'minimized' || store.settled() ? 'documentWorkspaces.common.close' : 'documentWorkspaces.common.minimize') | transloco\"\n (click)=\"dismiss(); $event.stopPropagation()\">\n <x-mark-icon />\n </button>\n </div>\n </header>\n\n @if (store.connection(); as notice) {\n <connection-banner [notice]=\"notice\" (retry)=\"notice.retry?.()\" />\n }\n\n @if (mode() !== \"expanded\" && !store.allDone() && !completingWorkspace()) {\n <div class=\"flex flex-wrap items-center gap-x-3 gap-y-1 px-4 py-3\" data-testid=\"stats\">\n @for (chip of chips(); track chip.kind) {\n @if ($index > 0) {\n <span class=\"h-3.5 w-px shrink-0 bg-(--stroke-primary-lighter)\" aria-hidden=\"true\"></span>\n }\n <span\n class=\"inline-flex items-center gap-1.5 text-xs whitespace-nowrap text-(--font-neutral-muted) data-[kind=done]:text-(--font-success-base) data-[kind=failed]:text-(--font-error-base)\"\n [attr.data-kind]=\"chip.kind\">\n @switch (chip.kind) {\n @case (\"uploading\") {\n <upload-icon class=\"size-3.5 shrink-0\" />\n }\n @case (\"indexing\") {\n <sync-icon class=\"size-3.5 shrink-0\" />\n }\n @case (\"done\") {\n <check-icon class=\"size-3.5 shrink-0\" />\n }\n @default {\n <circle-x-icon class=\"size-3.5 shrink-0\" />\n }\n }\n <span><workspace-message [message]=\"chip.message\" /></span>\n @if (chip.busy) {\n <activity-dots class=\"-ml-1.5\" />\n }\n </span>\n }\n </div>\n }\n\n @if (mode() === \"expanded\") {\n <div class=\"flex max-h-[60vh] flex-col overflow-y-auto\">\n @if (store.workspaces().length > 1) {\n <div class=\"flex items-center gap-2 border-b border-(--stroke-primary-lighter) px-3 py-2\" data-testid=\"workspace-nav\">\n <button\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"workspaceIndex() === 0\"\n [attr.aria-label]=\"'documentWorkspaces.tracker.previousWorkspace' | transloco\"\n (click)=\"moveWorkspace(-1)\">\n <chevron-left-icon />\n </button>\n <span class=\"flex min-w-0 flex-1 items-center justify-center gap-2 truncate text-xs font-semibold text-(--font-neutral-black)\">\n {{ currentWorkspace()?.name }}\n <span class=\"shrink-0 font-normal text-(--font-neutral-muted)\">{{ currentIndex() + 1 }}/{{ store.workspaces().length }}</span>\n </span>\n <button\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"currentIndex() >= store.workspaces().length - 1\"\n [attr.aria-label]=\"'documentWorkspaces.tracker.nextWorkspace' | transloco\"\n (click)=\"moveWorkspace(1)\">\n <chevron-right-icon />\n </button>\n </div>\n }\n @for (section of sections(); track section.status) {\n <upload-tracker-section\n class=\"border-(--stroke-primary-lighter) not-first:border-t\"\n [status]=\"section.status\"\n [uploads]=\"section.uploads\"\n [progress]=\"section.progress\"\n [open]=\"isOpen(section.status)\"\n (toggle)=\"toggleSection(section.status)\"\n (discard)=\"discard($event)\"\n (retry)=\"retry($event)\" />\n }\n </div>\n }\n </section>\n }\n `\n})\nexport class UploadTracker {\n protected readonly store = inject(UploadTrackerStore);\n private readonly service = inject(UploadService);\n\n protected readonly mode = signal<Mode>(\"inline\");\n protected readonly workspaceIndex = signal(0);\n /** Sections the user folded, keyed `<workspaceId>:<status>`; open by default. */\n private readonly folded = signal<ReadonlySet<string>>(new Set());\n\n /** What has just happened, shown for a moment where the file count usually is. */\n private readonly flash = signal<Message | null>(null);\n /** The same for a minimized panel, which has nowhere else to say it. */\n private readonly peek = signal<{ message: Message; tone: \"success\" | \"error\" } | null>(null);\n protected readonly completingWorkspace = signal<TrackedWorkspace | null>(null);\n protected readonly dismissing = signal(false);\n /** The pointer is over a minimized panel: it slides back up, to be clicked open. */\n private readonly hover = signal(false);\n /** The panel is on its way down to the corner: quicker than the way back. */\n protected readonly minimizing = signal(false);\n /** What the strip of a minimized panel shows, which is not always what it should: a change swaps it. */\n protected readonly peekShown = signal<Message>({ text: \"\" });\n protected readonly peekTone = signal<\"success\" | \"error\" | null>(null);\n protected readonly peekSwap = signal<\"shown\" | \"hidden\" | \"below\">(\"shown\");\n private peekKey = \"\";\n private hoverTimer: ReturnType<typeof setTimeout> | null = null;\n\n private readonly timers = new Set<ReturnType<typeof setTimeout>>();\n private flashTimer: ReturnType<typeof setTimeout> | null = null;\n private peekTimer: ReturnType<typeof setTimeout> | null = null;\n private peekTimer2: ReturnType<typeof setTimeout> | null = null;\n private dismissTimer: ReturnType<typeof setTimeout> | null = null;\n private completeTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** Status of each file as of the last change, to tell what just finished from what already had. */\n private previous = new Map<string, UploadStatus>();\n private previousPhase: string | null = null;\n\n // ── what to show ──\n\n protected readonly currentIndex = computed(() => Math.min(this.workspaceIndex(), Math.max(0, this.store.workspaces().length - 1)));\n protected readonly currentWorkspace = computed<TrackedWorkspace | null>(() => this.store.workspaces()[this.currentIndex()] ?? null);\n\n protected readonly sections = computed(() => {\n const workspace = this.currentWorkspace();\n if (!workspace) return [];\n const uploads = this.store.uploads().filter(u => u.workspaceId === workspace.id);\n const progress = this.store.progress()[workspace.id];\n return SECTION_ORDER.map(status => ({\n status,\n uploads: uploads.filter(u => u.status === status),\n // Only the two phases that move have a bar; the others never read it.\n progress: status === \"uploading\" || status === \"indexing\" ? progress?.[status] : undefined\n })).filter(section => section.uploads.length);\n });\n\n protected readonly header = computed<{ title: Message; subtitle: Message; tone: Tone }>(() => {\n const workspaces = this.store.workspaces();\n const completing = this.completingWorkspace();\n if (completing) return { title: { text: truncateName(completing.name, 28) }, subtitle: trackerMessage(\"complete\"), tone: \"success\" };\n\n const title: Message = workspaces.length === 1 ? { text: workspaces[0].name } : trackerMessage(\"workspaceCount\", { count: workspaces.length });\n // Everything indexed and nothing failed: the header carries the whole success message.\n if (this.store.allDone()) return { title, subtitle: trackerMessage(\"indexedFileCount\", { count: this.store.indexedCount() }), tone: \"success\" };\n const flash = this.flash();\n if (flash && this.mode() === \"inline\") return { title, subtitle: flash, tone: \"flash\" };\n return { title, subtitle: trackerMessage(\"fileCount\", { count: this.store.uploads().length }), tone: \"normal\" };\n });\n\n /** The counters of the compact view: what moves, what finished, what failed. */\n protected readonly chips = computed(() => {\n const groups = this.store.byStatus();\n const failed = this.store.failed();\n const severalWorkspaces = this.store.workspaces().length > 1;\n // One file is named, several are counted.\n const label = (uploads: readonly TrackedUpload[], key: string, max: number): Message =>\n uploads.length === 1 ? uploadLabel(uploads[0], max) : trackerMessage(key, { count: uploads.length });\n\n const chips: { kind: string; message: Message; busy: boolean }[] = [];\n if (groups.uploading.length) chips.push({ kind: \"uploading\", busy: true, message: trackerMessage(\"uploadingCount\", { count: groups.uploading.length }) });\n if (groups.indexing.length) chips.push({ kind: \"indexing\", busy: true, message: trackerMessage(\"indexingCount\", { count: groups.indexing.length }) });\n // With several workspaces, finished files are reported per workspace as each completes.\n if (groups.done.length && !severalWorkspaces) chips.push({ kind: \"done\", busy: false, message: label(groups.done, \"completedCount\", 26) });\n if (failed.length) chips.push({ kind: \"failed\", busy: false, message: label(failed, \"failedCount\", 26) });\n return chips;\n });\n\n /** What the minimized pill says: a notification while there is one, otherwise a summary. */\n protected readonly pillMessage = computed<Message>(() => {\n const peek = this.peek();\n if (peek) return peek.message;\n const failed = this.store.failed();\n if (failed.length) return failed.length === 1 ? uploadLabel(failed[0], 22) : trackerMessage(\"errorCount\", { count: failed.length });\n const { uploading, indexing, done } = this.store.byStatus();\n const active = uploading.length + indexing.length;\n if (done.length && !active) return done.length === 1 ? uploadLabel(done[0], 22) : trackerMessage(\"indexedCount\", { count: done.length });\n return trackerMessage(\"fileCount\", { count: active });\n });\n\n protected readonly state = computed(() => (this.mode() !== \"minimized\" ? \"normal\" : this.hover() ? \"minimized-hover\" : \"minimized\"));\n\n /** The strip's own colour: the failure, or the success, that stands. */\n protected readonly peekKind = computed(() => (this.store.allDone() ? \"done\" : this.store.failed().length ? \"errors\" : null));\n\n protected readonly canRetryAll = computed(() => this.store.uploads().some(u => u.status === \"error\" && u.retryable));\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.timers.forEach(clearTimeout));\n\n // React to what changed in the store: new files, files that just finished or failed.\n effect(() => {\n const uploads = this.store.uploads();\n untracked(() => this.onUploadsChanged(uploads));\n });\n\n // A workspace whose files are all indexed, while others are still going.\n effect(() => {\n const workspaces = this.store.workspaces();\n const uploads = this.store.uploads();\n const allDone = this.store.allDone();\n untracked(() => {\n if (allDone || workspaces.length < 2 || this.completingWorkspace()) return;\n const complete = workspaces.find(w => {\n const own = uploads.filter(u => u.workspaceId === w.id);\n return own.length > 0 && own.every(isFinished) && !own.some(isFailed);\n });\n if (complete) this.completeWorkspace(complete);\n });\n });\n\n // Everything is over: hold the outcome for a while, then go away — unless a failure of the\n // user's own is there to be read, which stays until they close it. A new upload cancels\n // this (nothing is settled any more), and the delay starts afresh when it ends.\n effect(() => {\n const leaves = this.store.settled() && !this.store.authorFailed();\n untracked(() => (leaves ? this.startAutoDismiss() : this.cancelAutoDismiss()));\n });\n\n // The strip of a minimized panel says what is going on; when that changes, the text is swapped, not cut.\n effect(() => {\n const message = this.pillMessage();\n const tone = this.peek()?.tone ?? null;\n untracked(() => this.syncPeek(message, tone));\n });\n\n // A minimized panel has no banner: a change of connection phase goes to the strip.\n effect(() => {\n const phase = this.store.connection()?.phase ?? null;\n untracked(() => {\n if (phase && phase !== this.previousPhase) this.notify(trackerMessage(phase === \"signed-out\" ? \"signedOut\" : \"connectionLost\"), \"error\");\n this.previousPhase = phase;\n });\n });\n }\n\n // ── what the user can do ──\n\n protected restore(): void {\n this.clearHover();\n this.mode.set(\"inline\");\n }\n\n protected onPanelClick(): void {\n if (this.mode() === \"minimized\") this.restore();\n }\n\n protected onPointerEnter(): void {\n if (this.mode() !== \"minimized\") return;\n if (this.hoverTimer) clearTimeout(this.hoverTimer);\n this.hoverTimer = null;\n this.hover.set(true);\n }\n\n protected onPointerLeave(): void {\n if (this.mode() !== \"minimized\") return;\n // Not at once: a pointer that strays off the edge while the panel slides should not make it flap.\n this.hoverTimer = this.replaceTimer(this.hoverTimer, HOVER_LEAVE_MS, () => {\n this.hoverTimer = null;\n this.hover.set(false);\n });\n }\n\n protected onTransitionEnd(event: TransitionEvent): void {\n if (event.target === event.currentTarget && event.propertyName === \"transform\") this.minimizing.set(false);\n }\n\n private clearHover(): void {\n if (this.hoverTimer) clearTimeout(this.hoverTimer);\n this.hoverTimer = null;\n this.hover.set(false);\n }\n\n protected toggleExpanded(): void {\n if (this.mode() === \"minimized\") {\n this.restore();\n return;\n }\n this.mode.update(mode => (mode === \"expanded\" ? \"inline\" : \"expanded\"));\n }\n\n /** Minimizes while there is work going on; once everything is over (failures included), closes for good. */\n protected dismiss(): void {\n if (this.store.settled() || this.mode() === \"minimized\") {\n this.reset();\n } else {\n this.clearHover();\n this.minimizing.set(true);\n this.mode.set(\"minimized\");\n }\n }\n\n protected moveWorkspace(step: -1 | 1): void {\n this.workspaceIndex.set(Math.max(0, Math.min(this.currentIndex() + step, this.store.workspaces().length - 1)));\n }\n\n protected isOpen(status: UploadStatus): boolean {\n return !this.folded().has(this.sectionKey(status));\n }\n\n protected toggleSection(status: UploadStatus): void {\n const key = this.sectionKey(status);\n this.folded.update(folded => {\n const next = new Set(folded);\n if (!next.delete(key)) next.add(key);\n return next;\n });\n }\n\n /**\n * Cancels a transfer in progress, or drops a row that is over. Only the\n * service knows the transfers it can still act on; a row it does not know\n * (finished, or failed on the server) is simply taken off the list.\n */\n protected async discard(id: string): Promise<void> {\n await this.service.cancel(id);\n this.store.remove(id);\n }\n\n protected retry(id: string): void {\n this.service.retry(id);\n }\n\n protected retryAll(): void {\n this.service.resumeStalled();\n }\n\n // ── moments that pass by themselves ──\n\n private onUploadsChanged(uploads: readonly TrackedUpload[]): void {\n const before = this.previous;\n this.previous = new Map(uploads.map(u => [u.id, u.status]));\n\n const added = uploads.filter(u => !before.has(u.id));\n if (added.length) {\n // New files: whatever was on screen makes way for them.\n this.cancelAutoDismiss();\n if (this.mode() !== \"inline\") this.mode.set(\"inline\");\n this.workspaceIndex.set(Math.max(0, this.store.workspaces().length - 1));\n }\n\n const finished = uploads.filter(u => before.has(u.id) && before.get(u.id) !== u.status);\n const done = finished.filter(u => u.status === \"done\");\n const failed = finished.filter(isFailed);\n\n if (done.length) {\n const last = done[done.length - 1];\n const message = uploadLabel(last, this.mode() === \"minimized\" ? 22 : 30);\n if (this.mode() === \"minimized\") this.notify(message, \"success\");\n else if (this.mode() === \"inline\" && !this.store.allDone()) this.showFlash(message);\n }\n if (failed.length && this.mode() === \"minimized\") {\n this.notify(uploadLabel(failed[failed.length - 1], 22), \"error\");\n }\n }\n\n /**\n * Puts `message` on the strip: the old text leaves upward, the new one comes up from below.\n * The first text is simply there, and what is already shown is left alone.\n */\n private syncPeek(message: Message, tone: \"success\" | \"error\" | null): void {\n const key = JSON.stringify([message, tone]);\n if (key === this.peekKey) return;\n const first = this.peekKey === \"\";\n this.peekKey = key;\n if (first) {\n this.peekShown.set(message);\n this.peekTone.set(tone);\n return;\n }\n this.peekSwap.set(\"hidden\");\n this.peekTimer2 = this.replaceTimer(this.peekTimer2, PEEK_SWAP_MS, () => {\n this.peekShown.set(message);\n this.peekTone.set(tone);\n this.peekSwap.set(\"below\");\n // A beat on the \"below\" position, so that the move up is seen.\n this.peekTimer2 = this.replaceTimer(this.peekTimer2, PEEK_REFLOW_MS, () => this.peekSwap.set(\"shown\"));\n });\n }\n\n private showFlash(message: Message): void {\n this.flash.set(message);\n this.flashTimer = this.replaceTimer(this.flashTimer, FLASH_MS, () => this.flash.set(null));\n }\n\n private notify(message: Message, tone: \"success\" | \"error\"): void {\n if (this.mode() !== \"minimized\") return;\n this.peek.set({ message, tone });\n this.peekTimer = this.replaceTimer(this.peekTimer, PEEK_MS, () => this.peek.set(null));\n }\n\n private completeWorkspace(workspace: TrackedWorkspace): void {\n if (this.mode() === \"minimized\") {\n this.notify(trackerMessage(\"workspaceIndexed\", { name: truncateName(workspace.name, 18) }), \"success\");\n this.store.removeWorkspace(workspace.id);\n return;\n }\n // Put the header in charge, so the success is the focus, then clear the workspace away.\n if (this.mode() === \"expanded\") this.mode.set(\"inline\");\n this.completingWorkspace.set(workspace);\n this.completeTimer = this.replaceTimer(this.completeTimer, WORKSPACE_COMPLETE_MS, () => {\n this.store.removeWorkspace(workspace.id);\n this.completingWorkspace.set(null);\n });\n }\n\n private startAutoDismiss(): void {\n if (this.dismissTimer) return;\n // The success message is in the header: make sure it can be seen. Failures are in the sections: leave those open.\n if (this.store.allDone()) this.mode.set(\"inline\");\n this.dismissTimer = this.replaceTimer(null, AUTO_DISMISS_MS, () => {\n this.dismissTimer = null;\n this.dismissing.set(true);\n this.replaceTimer(null, DISMISS_ANIMATION_MS, () => this.reset());\n });\n }\n\n private cancelAutoDismiss(): void {\n if (this.dismissTimer) clearTimeout(this.dismissTimer);\n this.dismissTimer = null;\n this.dismissing.set(false);\n }\n\n private reset(): void {\n this.cancelAutoDismiss();\n for (const timer of [this.flashTimer, this.peekTimer, this.completeTimer]) if (timer) clearTimeout(timer);\n this.flash.set(null);\n this.peek.set(null);\n this.completingWorkspace.set(null);\n this.folded.set(new Set());\n this.clearHover();\n this.minimizing.set(false);\n this.mode.set(\"inline\");\n this.workspaceIndex.set(0);\n this.store.reset();\n }\n\n private sectionKey(status: UploadStatus): string {\n return `${this.currentWorkspace()?.id}:${status}`;\n }\n\n /** Replaces a pending timer with a new one, all cleared together when the panel is destroyed. */\n private replaceTimer(current: ReturnType<typeof setTimeout> | null, ms: number, action: () => void): ReturnType<typeof setTimeout> {\n if (current) {\n clearTimeout(current);\n this.timers.delete(current);\n }\n const timer = setTimeout(() => {\n this.timers.delete(timer);\n action();\n }, ms);\n this.timers.add(timer);\n return timer;\n }\n}\n","import { assertInInjectionContext, DestroyRef, inject } from \"@angular/core\";\nimport { PollResult, StatusPoller } from \"../upload/status-poller\";\n\nexport interface LiveRefreshOptions {\n /** Delay between automatic refreshes while something keeps changing (default 1500ms, see {@link StatusPoller}). */\n intervalMs?: number;\n /** Ceiling the idle backoff climbs to when nothing changes between polls. */\n maxIntervalMs?: number;\n}\n\n/**\n * Keeps a view fresh while it stays open, without the user ever asking: `tick`\n * re-fetches whatever the view shows and reports whether the result differs\n * from before — the loop speeds back up on a change and backs off while idle,\n * exactly like {@link StatusPoller} already does for upload status.\n *\n * Paused while the browser reports no network (resumed the moment it is back),\n * and fully stopped once the injecting component is destroyed. The very first\n * scheduled tick is a no-op: whatever calls `start()` has normally just\n * fetched on its own, so re-fetching the same thing instantly would be\n * redundant — the first real poll happens one `intervalMs` later.\n *\n * Must be called from an injection context.\n */\nexport function injectLiveRefresh(tick: (signal: AbortSignal) => Promise<boolean>, options: LiveRefreshOptions = {}): { start(): void; stop(): void } {\n assertInInjectionContext(injectLiveRefresh);\n\n let shouldRun = false;\n let first = true;\n const poller = new StatusPoller({\n intervalMs: options.intervalMs,\n maxIntervalMs: options.maxIntervalMs,\n tick: async (signal): Promise<PollResult> => {\n if (first) {\n first = false;\n return { pending: true, changed: true };\n }\n return { pending: true, changed: await tick(signal) };\n }\n });\n\n if (typeof window !== \"undefined\") {\n const resume = () => {\n if (shouldRun) poller.start();\n };\n const pause = () => poller.stop();\n window.addEventListener(\"online\", resume);\n window.addEventListener(\"offline\", pause);\n inject(DestroyRef).onDestroy(() => {\n window.removeEventListener(\"online\", resume);\n window.removeEventListener(\"offline\", pause);\n });\n }\n\n inject(DestroyRef).onDestroy(() => poller.stop());\n\n return {\n start(): void {\n shouldRun = true;\n if (typeof navigator === \"undefined\" || navigator.onLine) poller.start();\n },\n stop(): void {\n shouldRun = false;\n poller.stop();\n }\n };\n}\n","import { ChangeDetectionStrategy, Component, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckboxComponent, FileIcon, TagComponent, TrashIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { HighlightedText } from \"../shared/highlighted-text\";\nimport { WorkspaceIcon } from \"../shared/workspace-icon\";\nimport { WorkspaceRoleTag } from \"../shared/workspace-role-tag\";\n\n/**\n * The columns of the list view — the header and every row share it.\n * Written out in full: Tailwind only generates the classes it can read as literals.\n */\nexport const WORKSPACE_ROW_COLUMNS =\n \"grid grid-cols-[minmax(0,1fr)_auto_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_auto_2.5rem] @xl:grid-cols-[minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @xl:data-selecting:grid-cols-[2rem_minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem]\";\n\n/** A workspace in the list view. Same contract as {@link WorkspaceCard}. */\n@Component({\n selector: \"workspace-row\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n CheckboxComponent,\n FileIcon,\n HighlightedText,\n TagComponent,\n TooltipDirective,\n TrashIcon,\n WorkspaceIcon,\n WorkspaceRoleTag,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n role: \"row\",\n class: `${WORKSPACE_ROW_COLUMNS} group border-(--stroke-primary-lighter) bg-(--bg-neutral-white) cursor-pointer rounded-(--radius-md) border py-3 transition-shadow hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)] data-selected:border-(--stroke-primary-base) data-selected:bg-(--bg-primary-lightest) data-locked:cursor-default`,\n \"[attr.data-role]\": \"workspace().currentUserRole\",\n \"[attr.data-selecting]\": \"selecting() ? '' : null\",\n \"[attr.data-selected]\": \"selected() ? '' : null\",\n \"[attr.data-locked]\": \"selecting() && !selectable() ? '' : null\",\n \"(click)\": \"onRowClick($event)\"\n },\n template: `\n @if (selecting()) {\n <div role=\"cell\">\n <checkbox\n [checked]=\"selected()\"\n [disabled]=\"!selectable()\"\n [aria-label]=\"workspace().name\"\n (checkedChange)=\"selectedChange.emit($event)\" />\n </div>\n }\n\n <div role=\"cell\" class=\"flex min-w-0 items-center gap-3\">\n <workspace-icon size=\"md\" [color]=\"workspace().color\" />\n <div class=\"flex min-w-0 flex-col gap-px\">\n <button variant=\"none\" type=\"button\" class=\"block max-w-full truncate text-left text-sm font-semibold text-(--font-neutral-black)\" (click)=\"activate()\">\n <highlighted-text [text]=\"workspace().name\" [query]=\"query()\" />\n </button>\n <span class=\"truncate text-xs text-(--font-primary-muted)\">{{ workspace().description }}</span>\n </div>\n </div>\n\n <div role=\"cell\" class=\"flex items-center gap-1 text-xs text-(--font-primary-base)\">\n <file-icon class=\"size-3 text-(--font-primary-muted)\" />\n {{ workspace().documentCount }}\n </div>\n\n <div role=\"cell\"><workspace-role-tag [role]=\"workspace().currentUserRole\" /></div>\n\n <!-- The API has no update date for a workspace: the column is kept, as in the web element, and says so. -->\n <div role=\"cell\" class=\"text-xs text-(--font-primary-muted) hidden @xl:block\" data-testid=\"updated\">—</div>\n\n <div role=\"cell\" class=\"flex min-w-0 flex-nowrap items-center gap-1 overflow-hidden hidden @3xl:flex\">\n @for (tag of workspace().tags; track tag) {\n <tag scheme=\"grey\" size=\"xs\">{{ tag }}</tag>\n }\n </div>\n\n <div role=\"cell\" class=\"flex justify-end\">\n @if (!selecting()) {\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n class=\"text-(--font-error-muted) opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-(--bg-error-lightest) hover:text-(--font-error-base) max-md:opacity-100\"\n [iconOnly]=\"true\"\n [writerOnly]=\"workspace().currentUserRole\"\n [attr.aria-label]=\"'documentWorkspaces.list.deleteWorkspace' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.delete' | transloco\"\n (click)=\"deleteRequested.emit()\">\n <trash-icon />\n </button>\n }\n </div>\n `\n})\nexport class WorkspaceRow {\n readonly workspace = input.required<Workspace>();\n readonly query = input(\"\");\n readonly selecting = input(false);\n readonly selected = input(false);\n readonly selectable = input(true);\n\n readonly opened = output<void>();\n readonly deleteRequested = output<void>();\n readonly selectedChange = output<boolean>();\n\n protected activate(): void {\n if (this.selecting()) {\n if (this.selectable()) this.selectedChange.emit(!this.selected());\n } else {\n this.opened.emit();\n }\n }\n\n protected onRowClick(event: MouseEvent): void {\n if ((event.target as HTMLElement).closest(\"button, input, label\")) return;\n this.activate();\n }\n}\n","import { ChangeDetectionStrategy, Component, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckboxComponent, FileIcon, TagComponent, TrashIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { HighlightedText } from \"../shared/highlighted-text\";\nimport { WorkspaceIcon } from \"../shared/workspace-icon\";\nimport { WorkspaceRoleTag } from \"../shared/workspace-role-tag\";\n\n/**\n * A workspace in the grid.\n *\n * It only reports what the user asked for — open it, delete it, tick it — and\n * leaves doing it to the list. Opening is on the title, a real button, so the\n * keyboard reaches it; the whole card is clickable as a convenience for the pointer.\n */\n@Component({\n selector: \"workspace-card\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n CheckboxComponent,\n FileIcon,\n HighlightedText,\n TagComponent,\n TooltipDirective,\n TrashIcon,\n WorkspaceIcon,\n WorkspaceRoleTag,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class:\n \"group border-(--stroke-primary-lighter) bg-(--bg-neutral-white) flex cursor-pointer flex-col rounded-(--radius-md) border p-4 transition-shadow hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)] data-selected:border-(--stroke-primary-base) data-selected:bg-(--bg-primary-lightest) data-locked:cursor-default\",\n \"[attr.data-role]\": \"workspace().currentUserRole\",\n \"[attr.data-selected]\": \"selected() ? '' : null\",\n // In selection mode a workspace that cannot be selected is shown as such instead of silently ignoring the click.\n \"[attr.data-locked]\": \"selecting() && !selectable() ? '' : null\",\n \"(click)\": \"onCardClick($event)\"\n },\n template: `\n <div class=\"mb-3 flex items-start gap-3\">\n <workspace-icon [color]=\"workspace().color\" />\n <h3 class=\"min-w-0 flex-1 pt-2 text-base leading-[22px] font-semibold text-(--font-neutral-black)\">\n <button variant=\"none\" type=\"button\" class=\"block max-w-full truncate text-left\" (click)=\"activate()\">\n <highlighted-text [text]=\"workspace().name\" [query]=\"query()\" />\n </button>\n </h3>\n @if (selecting()) {\n <checkbox\n [checked]=\"selected()\"\n [disabled]=\"!selectable()\"\n [aria-label]=\"workspace().name\"\n (checkedChange)=\"selectedChange.emit($event)\" />\n } @else {\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n class=\"text-(--font-error-muted) opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-(--bg-error-lightest) hover:text-(--font-error-base) max-md:opacity-100\"\n [iconOnly]=\"true\"\n [writerOnly]=\"workspace().currentUserRole\"\n [attr.aria-label]=\"'documentWorkspaces.list.deleteWorkspace' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.delete' | transloco\"\n (click)=\"deleteRequested.emit()\">\n <trash-icon />\n </button>\n }\n </div>\n\n @if (workspace().tags.length) {\n <div class=\"mb-3 flex flex-wrap items-center gap-1\">\n @for (tag of workspace().tags; track tag) {\n <tag scheme=\"grey\" size=\"xs\">{{ tag }}</tag>\n }\n </div>\n }\n\n <p class=\"mb-3 line-clamp-4 flex-1 text-sm leading-[18px] text-(--font-primary-base)\">{{ workspace().description }}</p>\n\n <div class=\"mb-3 h-px bg-(--stroke-primary-lighter)\"></div>\n <div class=\"flex items-center justify-between gap-2\">\n <workspace-role-tag [role]=\"workspace().currentUserRole\" />\n <span class=\"ml-auto flex shrink-0 items-center gap-1 text-xs text-(--font-primary-muted)\">\n <file-icon class=\"size-3\" />\n {{ workspace().documentCount }}\n </span>\n </div>\n `\n})\nexport class WorkspaceCard {\n readonly workspace = input.required<Workspace>();\n /** The search box's text, to mark in the name. */\n readonly query = input(\"\");\n /** The list is in selection mode: checkboxes show, and a click ticks instead of opening. */\n readonly selecting = input(false);\n readonly selected = input(false);\n /** Whether this workspace may be ticked — not one shared read-only. */\n readonly selectable = input(true);\n\n readonly opened = output<void>();\n readonly deleteRequested = output<void>();\n readonly selectedChange = output<boolean>();\n\n /** Opens the workspace — or, in selection mode, ticks it. */\n protected activate(): void {\n if (this.selecting()) {\n if (this.selectable()) this.selectedChange.emit(!this.selected());\n } else {\n this.opened.emit();\n }\n }\n\n protected onCardClick(event: MouseEvent): void {\n // A button or the checkbox has handled it already.\n if ((event.target as HTMLElement).closest(\"button, input, label\")) return;\n this.activate();\n }\n}\n","import { ChangeDetectionStrategy, Component, inject, Injector, output, signal } from \"@angular/core\";\nimport { form, FormField } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe, TranslocoService } from \"@jsverse/transloco\";\nimport {\n BadgeComponent,\n ButtonComponent,\n CheckboxComponent,\n GridIcon,\n ListIcon,\n PlusIcon,\n SearchInputComponent,\n TabComponent,\n TabListComponent,\n TabsComponent,\n TrashIcon,\n UserIcon,\n UserRoundIcon,\n TooltipDirective\n} from \"@sinequa/galactik\";\nimport { injectLiveRefresh } from \"../../composables/inject-live-refresh\";\nimport { injectSelection } from \"../../composables/inject-selection\";\nimport { injectWorkspaceFilter, WorkspaceTab } from \"../../composables/inject-workspace-filter\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { UploadService } from \"../../upload/upload.service\";\nimport { workspaceErrorMessage } from \"../../utils/error-message\";\nimport { Message } from \"../../utils/message\";\nimport { canWrite } from \"../../utils/workspace-role\";\nimport { ConfirmAction, ConfirmDialogConfig } from \"../shared/confirm.dialog\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\nimport { WORKSPACE_ROW_COLUMNS, WorkspaceRow } from \"./workspace-row\";\nimport { WorkspaceCard } from \"./workspace-card\";\n\ntype ViewMode = \"grid\" | \"list\";\n\n/** The tabs, in order: the translation key of each one's label, and the badge count it carries. */\nconst TABS: readonly { tab: WorkspaceTab; labelKey: string }[] = [\n { tab: \"all\", labelKey: \"documentWorkspaces.list.tabAll\" },\n { tab: \"editor\", labelKey: \"documentWorkspaces.list.tabEditor\" },\n { tab: \"reader\", labelKey: \"documentWorkspaces.list.tabReader\" }\n];\n\n/**\n * The home of the workspaces view: every workspace you can reach, as a grid or a\n * list, filtered by tab and by search, with multi-selection and deletion.\n *\n * It reads `WorkspacesStore` and does not know how a workspace is opened or\n * created — it says so (`opened`, `createRequested`) and whoever hosts it decides.\n *\n * Each time it is shown it refreshes: workspaces created by others since the last\n * time appear, and the document counts match the server. The list already on\n * screen stays until the answer lands, and a failure leaves it untouched behind\n * an inline error. While it stays shown, it keeps polling in the background\n * (`injectLiveRefresh`) so a workspace created by someone else — one the current\n * user only reads, say — appears on its own, with no action needed.\n */\n@Component({\n selector: \"workspace-list\",\n imports: [\n TranslocoPipe,\n FormField,\n BadgeComponent,\n ButtonComponent,\n CheckboxComponent,\n GridIcon,\n ListIcon,\n PlusIcon,\n SearchInputComponent,\n TabComponent,\n TabListComponent,\n TabsComponent,\n TooltipDirective,\n TrashIcon,\n UserIcon,\n UserRoundIcon,\n WorkspaceCard,\n WorkspaceMessage,\n WorkspaceRow\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"@container flex h-full min-h-0 flex-col p-3 pb-2 md:p-8 md:pb-4\" },\n template: `\n <div class=\"mb-1 flex items-center justify-between gap-4\">\n <h1 class=\"text-2xl leading-[31px] font-bold text-(--font-neutral-black)\">{{ \"documentWorkspaces.list.title\" | transloco }}</h1>\n <button (click)=\"createRequested.emit()\">\n <plus-icon />\n {{ \"documentWorkspaces.list.addWorkspace\" | transloco }}\n </button>\n </div>\n <p class=\"mb-6 text-base text-(--font-primary-base)\">{{ \"documentWorkspaces.list.subtitle\" | transloco }}</p>\n\n <Tabs class=\"mb-3 max-w-full\">\n <TabList\n variant=\"inner\"\n class=\"w-fit max-w-full\"\n selectionMode=\"explicit\"\n [softDisabled]=\"false\"\n [attr.aria-label]=\"'documentWorkspaces.list.title' | transloco\"\n [selectedTab]=\"filter.tab()\"\n (selectedTabChange)=\"onTabChange($event)\">\n @for (item of tabs; track item.tab) {\n <Tab [value]=\"item.tab\" variant=\"inner\">\n @switch (item.tab) {\n @case (\"editor\") {\n <user-icon class=\"shrink-0\" />\n }\n @case (\"reader\") {\n <user-round-icon class=\"shrink-0\" />\n }\n }\n <span>{{ item.labelKey | transloco }}</span>\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"shrink-0\" [attr.data-testid]=\"'badge-' + item.tab\">{{ filter.counts()[item.tab] }}</Badge>\n </Tab>\n }\n </TabList>\n </Tabs>\n\n <div class=\"mb-6 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <SearchInput\n role=\"search\"\n class=\"min-w-32 flex-[0_1_15rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <span class=\"text-sm text-(--font-primary-base)\" aria-live=\"polite\">{{ \"documentWorkspaces.list.selectedCount\" | transloco: { count: selection.count() } }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <trash-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the workspaces scroll: the title, the tabs and the toolbar stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-sm text-(--font-primary-muted)\">{{ \"documentWorkspaces.list.loading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.list.loadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (!filter.filtered().length) {\n <p class=\"py-8 text-center text-sm text-(--font-primary-muted)\" data-testid=\"empty\">\n {{ (store.workspaces().length ? \"documentWorkspaces.list.noMatch\" : \"documentWorkspaces.list.empty\") | transloco }}\n </p>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] content-start gap-4 pb-8\">\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-card\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"${WORKSPACE_ROW_COLUMNS} py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <checkbox\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (checkedChange)=\"selection.toggleAll()\" />\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnName\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnContent\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnOwner\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @xl:block\">{{ \"documentWorkspaces.list.columnDateUpdated\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @3xl:block\">{{ \"documentWorkspaces.list.columnTags\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-row\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n }\n\n @if (store.hasMore() || store.truncated()) {\n <div class=\"flex flex-col items-center gap-2 pb-8\" data-testid=\"load-more\">\n <p class=\"text-sm text-(--font-primary-muted)\">\n {{ \"documentWorkspaces.list.partial\" | transloco: { shown: store.workspaces().length, total: store.pagination().total } }}\n </p>\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.loadMoreError\" | transloco: { error: message } }}</p>\n }\n @if (store.hasMore()) {\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.list.loadMore\" | transloco }}\n </button>\n }\n </div>\n }\n }\n }\n </div>\n `\n})\nexport class WorkspaceList {\n /** A workspace was chosen for opening. */\n readonly opened = output<Workspace>();\n /** The user wants a new workspace. Creating one is the host's dialog. */\n readonly createRequested = output<void>();\n\n protected readonly store = inject(WorkspacesStore);\n private readonly uploads = inject(UploadService);\n private readonly injector = inject(Injector);\n private readonly transloco = inject(TranslocoService);\n\n protected readonly tabs = TABS;\n protected readonly viewMode = signal<ViewMode>(\"grid\");\n protected readonly status = signal<\"loading\" | \"ready\" | \"error\">(\"loading\");\n protected readonly loadError = signal<Message | null>(null);\n protected readonly refreshError = signal<string | null>(null);\n protected readonly loadingMore = signal(false);\n protected readonly loadMoreError = signal<string | null>(null);\n\n protected readonly filter = injectWorkspaceFilter(this.store.workspaces);\n /** The search box, bound to the filter's own query signal. */\n protected readonly searchForm = form(this.filter.query);\n // A workspace shared read-only cannot be deleted: it is not offered for selection either, so the\n // count, \"select all\" and the Delete button never include one.\n protected readonly selection = injectSelection({\n items: this.filter.filtered,\n key: (workspace: Workspace) => workspace.id,\n selectable: workspace => canWrite(workspace.currentUserRole)\n });\n\n // Background polling, started once the list has something to compare against:\n // each tick re-reloads and compares the full result to what was cached before\n // it, not just the set of ids — a workspace renamed, re-tagged or whose access\n // changed keeps the same id, and would otherwise look unchanged forever, which\n // would both hide the edit and widen the backoff on ticks that are not actually idle.\n private readonly liveRefresh = injectLiveRefresh(\n async () => {\n const before = JSON.stringify(this.store.workspaces());\n await this.store.reload();\n const changed = JSON.stringify(this.store.workspaces()) !== before;\n if (changed) this.selection.exit(); // the ticks belonged to the list that was there before\n // An upload someone else is running on a workspace listed here: only the status API tells.\n // It stops by itself after a tick when nothing is moving.\n this.uploads.startPolling();\n return changed;\n },\n { intervalMs: 20_000, maxIntervalMs: 120_000 }\n );\n\n constructor() {\n this.uploads.startPolling();\n void this.load();\n }\n\n /** Shows what is cached at once and refreshes behind it, or loads for the first time. */\n protected async load(): Promise<void> {\n this.loadError.set(null);\n if (this.store.loaded()) {\n this.status.set(\"ready\");\n await this.refresh();\n this.liveRefresh.start();\n return;\n }\n\n this.status.set(\"loading\");\n try {\n await this.store.ensureLoaded();\n this.status.set(\"ready\");\n this.liveRefresh.start();\n } catch (error) {\n this.loadError.set(workspaceErrorMessage(error));\n this.status.set(\"error\");\n }\n }\n\n private async refresh(): Promise<void> {\n this.refreshError.set(null);\n try {\n await this.store.reload();\n // The ticks belonged to the list that was on screen.\n this.selection.exit();\n } catch (error) {\n this.refreshError.set(this.translate(workspaceErrorMessage(error)));\n }\n }\n\n protected async loadMore(): Promise<void> {\n if (this.loadingMore()) return;\n this.loadMoreError.set(null);\n this.loadingMore.set(true);\n try {\n await this.store.loadMore();\n } catch (error) {\n this.loadMoreError.set(this.translate(workspaceErrorMessage(error)));\n } finally {\n this.loadingMore.set(false);\n }\n }\n\n protected onTabChange(tab: string | undefined): void {\n if (tab) this.filter.tab.set(tab as WorkspaceTab);\n }\n\n protected deleteSelected(): void {\n void this.confirmDelete(this.selection.selectedItems());\n }\n\n /**\n * Asks, then deletes. The API only has a bulk DELETE, so one workspace is a\n * one-element batch. If some could not be deleted the dialog stays open and says\n * which, and a retry only targets those, so it does not re-attempt ones already gone.\n */\n protected async confirmDelete(targets: Workspace[]): Promise<void> {\n if (!targets.length) return;\n const names = new Map(targets.map(w => [w.id, w.name]));\n let remaining = targets.map(w => w.id);\n\n const single = targets.length === 1;\n const subject = single ? targets[0].name : this.transloco.translate(\"documentWorkspaces.list.workspaceCount\", { count: targets.length });\n const config: ConfirmDialogConfig = {\n title: single\n ? this.transloco.translate(\"documentWorkspaces.list.deleteTitle\")\n : this.transloco.translate(\"documentWorkspaces.list.deleteWorkspacesTitle\", { count: targets.length }),\n description: this.transloco.translate(\"documentWorkspaces.common.deleteWarning\", { name: subject }),\n emphasis: subject,\n destructive: true,\n action: async () => {\n const { failed } = await this.store.remove(remaining);\n remaining = failed.map(f => f.id);\n if (failed.length) {\n throw new Error(\n this.transloco.translate(\"documentWorkspaces.list.bulkDeleteFailed\", {\n count: failed.length,\n names: failed.map(f => names.get(f.id) ?? f.id).join(\", \")\n })\n );\n }\n }\n };\n\n const result = await ConfirmAction.call(config, { injector: this.injector });\n if (result === \"dialog-confirm\" && !single) this.selection.exit();\n }\n\n private translate(message: Message): string {\n return \"key\" in message ? this.transloco.translate(message.key, message.params) : message.text;\n }\n}\n","import { ChangeDetectionStrategy, Component, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckboxComponent, TagComponent, TrashIcon, UploadIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport { injectDocumentLabels } from \"../../composables/inject-document-labels\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { WorkspaceDocument, WorkspaceRole } from \"../../models/workspace.model\";\nimport { DocumentStatusBadge } from \"../shared/document-status-badge\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\nimport { HighlightedText } from \"../shared/highlighted-text\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\n\n/**\n * A document in the grid.\n *\n * Like `WorkspaceCard` it only reports what the user asked for — open it,\n * delete it, tick it — and leaves doing it to the detail view. The name is a real\n * button so the keyboard reaches it; the whole card is clickable for the pointer.\n */\n@Component({\n selector: \"document-card\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n CheckboxComponent,\n DocumentStatusBadge,\n DocumentTypeIcon,\n HighlightedText,\n TagComponent,\n TooltipDirective,\n TrashIcon,\n UploadIcon,\n WorkspaceMessage,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class:\n \"group border-(--stroke-primary-lighter) bg-(--bg-neutral-white) relative flex cursor-pointer flex-col rounded-(--radius-md) border p-4 transition-shadow hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)] data-active:border-(--stroke-primary-base) data-selected:border-(--stroke-primary-base) data-selected:bg-(--bg-primary-lightest) data-locked:cursor-default\",\n \"[attr.data-status]\": \"document().status ?? null\",\n \"[attr.data-active]\": \"active() ? '' : null\",\n \"[attr.data-selected]\": \"selected() ? '' : null\",\n \"[attr.data-locked]\": \"selecting() && !selectable() ? '' : null\",\n \"(click)\": \"onCardClick($event)\"\n },\n template: `\n <div class=\"mb-3 flex items-start gap-3\">\n <document-type-icon [type]=\"document().type\" />\n <span class=\"flex-1\"></span>\n @if (selecting()) {\n <checkbox\n [checked]=\"selected()\"\n [disabled]=\"!selectable()\"\n [aria-label]=\"document().name\"\n (checkedChange)=\"selectedChange.emit($event)\" />\n } @else {\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n class=\"text-(--font-error-muted) opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-(--bg-error-lightest) hover:text-(--font-error-base) max-md:opacity-100\"\n [iconOnly]=\"true\"\n [writerOnly]=\"workspaceRole()\"\n [attr.aria-label]=\"'documentWorkspaces.detail.deleteDocument' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.delete' | transloco\"\n (click)=\"deleteRequested.emit()\">\n <trash-icon />\n </button>\n }\n </div>\n\n <h3 class=\"mb-2 truncate text-sm leading-[18px] font-semibold text-(--font-neutral-black)\">\n <button variant=\"none\" type=\"button\" class=\"block max-w-full truncate text-left\" [attr.title]=\"document().name\" (click)=\"activate()\">\n <highlighted-text [text]=\"document().name\" [query]=\"query()\" />\n </button>\n </h3>\n <span class=\"min-h-4 text-xs text-(--font-primary-muted)\" data-testid=\"size\">{{ labels.size() ?? \"—\" }}</span>\n\n <div class=\"my-3 h-px bg-(--stroke-primary-lighter)\"></div>\n <div class=\"flex items-center justify-between gap-2 text-xs\">\n <tag scheme=\"grey\" size=\"xs\" class=\"min-w-0\" [hidden]=\"!document().uploadedBy && !document().source\">\n @if (document().uploadedBy) {\n <upload-icon class=\"size-3 shrink-0\" />\n <span class=\"truncate\">{{ \"documentWorkspaces.detail.uploadedBy\" | transloco: { name: document().uploadedBy } }}</span>\n } @else {\n <span class=\"truncate\">{{ document().source }}</span>\n }\n </tag>\n @if (document().status) {\n <document-status-badge class=\"ml-auto\" [status]=\"document().status\" />\n } @else if (labels.added(); as added) {\n <span class=\"ml-auto shrink-0 text-(--font-primary-muted)\"><workspace-message [message]=\"added\" /></span>\n }\n </div>\n `\n})\nexport class DocumentCard {\n readonly document = input.required<WorkspaceDocument>();\n /** The role of the current user in the workspace — the delete button is for writers. */\n readonly workspaceRole = input.required<WorkspaceRole | null | undefined>();\n /** The search box's text, to mark in the name. */\n readonly query = input(\"\");\n /** The one open in the preview. */\n readonly active = input(false);\n readonly selecting = input(false);\n readonly selected = input(false);\n readonly selectable = input(true);\n\n readonly opened = output<void>();\n readonly deleteRequested = output<void>();\n readonly selectedChange = output<boolean>();\n\n protected readonly labels = injectDocumentLabels(this.document);\n\n /** Opens the document — or, in selection mode, ticks it. */\n protected activate(): void {\n if (this.selecting()) {\n if (this.selectable()) this.selectedChange.emit(!this.selected());\n } else {\n this.opened.emit();\n }\n }\n\n protected onCardClick(event: MouseEvent): void {\n // A button or the checkbox has handled it already.\n if ((event.target as HTMLElement).closest(\"button, input, label\")) return;\n this.activate();\n }\n}\n","import { ChangeDetectionStrategy, Component, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckboxComponent, TagComponent, TrashIcon, UploadIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport { injectDocumentLabels } from \"../../composables/inject-document-labels\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { WorkspaceDocument, WorkspaceRole } from \"../../models/workspace.model\";\nimport { DocumentStatusBadge } from \"../shared/document-status-badge\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\nimport { HighlightedText } from \"../shared/highlighted-text\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\n\n/**\n * The columns of the document list — the header and every row share it.\n * Written out in full: Tailwind only generates the classes it can read as literals.\n */\nexport const DOCUMENT_ROW_COLUMNS =\n \"grid grid-cols-[minmax(0,1fr)_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_minmax(0,1fr)_6rem_10rem_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_minmax(0,1fr)_6rem_10rem_2.5rem]\";\n\n/** A document in the list view. Same contract as `DocumentCard`. */\n@Component({\n selector: \"document-row\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n CheckboxComponent,\n DocumentStatusBadge,\n DocumentTypeIcon,\n HighlightedText,\n TagComponent,\n TooltipDirective,\n TrashIcon,\n UploadIcon,\n WorkspaceMessage,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n role: \"row\",\n class: `${DOCUMENT_ROW_COLUMNS} group border-(--stroke-primary-lighter) bg-(--bg-neutral-white) cursor-pointer rounded-(--radius-md) border py-2.5 text-sm transition-shadow hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)] data-active:border-(--stroke-primary-base) data-selected:border-(--stroke-primary-base) data-selected:bg-(--bg-primary-lightest) data-locked:cursor-default`,\n \"[attr.data-status]\": \"document().status ?? null\",\n \"[attr.data-selecting]\": \"selecting() ? '' : null\",\n \"[attr.data-active]\": \"active() ? '' : null\",\n \"[attr.data-selected]\": \"selected() ? '' : null\",\n \"[attr.data-locked]\": \"selecting() && !selectable() ? '' : null\",\n \"(click)\": \"onRowClick($event)\"\n },\n template: `\n @if (selecting()) {\n <div role=\"cell\">\n <checkbox\n [checked]=\"selected()\"\n [disabled]=\"!selectable()\"\n [aria-label]=\"document().name\"\n (checkedChange)=\"selectedChange.emit($event)\" />\n </div>\n }\n\n <div role=\"cell\" class=\"flex min-w-0 items-center gap-3\">\n <document-type-icon size=\"md\" [type]=\"document().type\" />\n <button variant=\"none\" type=\"button\" class=\"block max-w-full truncate text-left font-semibold text-(--font-neutral-black)\" (click)=\"activate()\">\n <highlighted-text [text]=\"document().name\" [query]=\"query()\" />\n </button>\n </div>\n\n <div role=\"cell\" class=\"min-w-0 @max-3xl:hidden\">\n @if (document().uploadedBy) {\n <tag scheme=\"grey\" size=\"xs\">\n <upload-icon class=\"size-3 shrink-0\" />\n <span class=\"truncate\">{{ \"documentWorkspaces.detail.uploadedBy\" | transloco: { name: document().uploadedBy } }}</span>\n </tag>\n } @else if (document().source) {\n <tag scheme=\"grey\" size=\"xs\">{{ document().source }}</tag>\n }\n </div>\n\n <div role=\"cell\" class=\"text-xs text-(--font-primary-muted) @max-3xl:hidden\" data-testid=\"size\">{{ labels.size() ?? \"—\" }}</div>\n\n <div role=\"cell\" class=\"text-xs text-(--font-primary-base)\">\n @if (document().status) {\n <document-status-badge [status]=\"document().status\" />\n } @else if (labels.added(); as added) {\n <workspace-message [message]=\"added\" />\n }\n </div>\n\n <div role=\"cell\" class=\"flex justify-end\">\n @if (!selecting()) {\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n class=\"text-(--font-error-muted) opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-(--bg-error-lightest) hover:text-(--font-error-base) max-md:opacity-100\"\n [iconOnly]=\"true\"\n [writerOnly]=\"workspaceRole()\"\n [attr.aria-label]=\"'documentWorkspaces.detail.deleteDocument' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.delete' | transloco\"\n (click)=\"deleteRequested.emit()\">\n <trash-icon />\n </button>\n }\n </div>\n `\n})\nexport class DocumentRow {\n readonly document = input.required<WorkspaceDocument>();\n readonly workspaceRole = input.required<WorkspaceRole | null | undefined>();\n readonly query = input(\"\");\n readonly active = input(false);\n readonly selecting = input(false);\n readonly selected = input(false);\n readonly selectable = input(true);\n\n readonly opened = output<void>();\n readonly deleteRequested = output<void>();\n readonly selectedChange = output<boolean>();\n\n protected readonly labels = injectDocumentLabels(this.document);\n\n protected activate(): void {\n if (this.selecting()) {\n if (this.selectable()) this.selectedChange.emit(!this.selected());\n } else {\n this.opened.emit();\n }\n }\n\n protected onRowClick(event: MouseEvent): void {\n if ((event.target as HTMLElement).closest(\"button, input, label\")) return;\n this.activate();\n }\n}\n","import type { AssignableRole, Member } from \"../models/share.model\";\nimport type { Workspace } from \"../models/workspace.model\";\nimport type { ResolvedPrincipal } from \"../services/principal-directory.service\";\n\ntype Access = Pick<Workspace, \"creatorId\" | \"ownerIds\" | \"readerIds\">;\n\n/**\n * The access list of a workspace: one row per distinct id, the creator pinned to\n * the top and everyone else by name.\n *\n * The creator is normally in `ownerIds` as well, so without the dedupe they would\n * show up twice — once as the immutable creator and once as an ordinary owner.\n * An id in both arrays counts as an owner.\n */\nexport function buildMembers(workspace: Access, resolve: (id: string) => ResolvedPrincipal): Member[] {\n const seen = new Set<string>();\n const members: Member[] = [];\n const add = (id: string, role: Member[\"role\"]): void => {\n if (!id || seen.has(id)) return;\n seen.add(id);\n const { name, email, isGroup, resolved, isSelf } = resolve(id);\n members.push({ id, name, email, isGroup, isSelf, role, resolved });\n };\n add(workspace.creatorId, \"creator\");\n workspace.ownerIds.forEach(id => add(id, \"owner\"));\n workspace.readerIds.forEach(id => add(id, \"reader\"));\n\n return members.sort((a, b) => Number(b.role === \"creator\") - Number(a.role === \"creator\") || a.name.localeCompare(b.name));\n}\n\n/**\n * The `owners`/`readers` arrays after giving `id` a role, or taking its access away.\n *\n * The API replaces both arrays wholesale, so they carry every id the workspace\n * already holds — the creator's, and ids that could not be resolved to a name —\n * not just the ones the list shows: dropping one would silently revoke it.\n */\nexport function withRole(\n workspace: Pick<Workspace, \"ownerIds\" | \"readerIds\">,\n id: string,\n role: AssignableRole | \"remove\"\n): { owners: string[]; readers: string[] } {\n const owners = workspace.ownerIds.filter(o => o !== id);\n const readers = workspace.readerIds.filter(r => r !== id);\n if (role === \"owner\") owners.push(id);\n else if (role === \"reader\") readers.push(id);\n return { owners, readers };\n}\n\n/** The arrays after adding the picked people: the ids already there stay, the picked ones join their role's array. */\nexport function withAdded(\n workspace: Pick<Workspace, \"ownerIds\" | \"readerIds\">,\n picked: readonly { id: string; role: AssignableRole }[]\n): { owners: string[]; readers: string[] } {\n const ids = new Set(picked.map(p => p.id));\n return {\n owners: [...workspace.ownerIds.filter(id => !ids.has(id)), ...picked.filter(p => p.role === \"owner\").map(p => p.id)],\n readers: [...workspace.readerIds.filter(id => !ids.has(id)), ...picked.filter(p => p.role === \"reader\").map(p => p.id)]\n };\n}\n\n/** `AB` for \"Alice Brown\", `AL` for \"alice\", `?` for nothing. */\nexport function initialsOf(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (!parts.length) return \"?\";\n return (parts.length === 1 ? parts[0].slice(0, 2) : parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { AvatarComponent, AvatarFallbackComponent, UserRoundIcon } from \"@sinequa/galactik\";\nimport { initialsOf } from \"../../utils/members\";\n\n/** A small round avatar: the initials of a person, a glyph for a group. */\n@Component({\n selector: \"principal-avatar\",\n imports: [AvatarComponent, AvatarFallbackComponent, UserRoundIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"inline-flex shrink-0\", \"aria-hidden\": \"true\" },\n template: `\n <avatar size=\"medium\" fill=\"secondary\" scheme=\"sage\">\n <AvatarFallback>\n @if (group()) {\n <user-round-icon />\n } @else {\n {{ initials() }}\n }\n </AvatarFallback>\n </avatar>\n `\n})\nexport class PrincipalAvatar {\n readonly name = input.required<string>();\n readonly group = input(false);\n\n protected readonly initials = computed(() => initialsOf(this.name()));\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n ChevronDownIcon,\n EyeIcon,\n KeyIcon,\n MenuComponent,\n MenuContentComponent,\n MenuItemComponent,\n MenuTriggerDirective\n} from \"@sinequa/galactik\";\nimport type { AssignableRole } from \"../../models/share.model\";\n\n/**\n * \"Owner ▾\" / \"Reader ▾\": a menu to pick a role, and — for someone who already has\n * access — to take it away.\n *\n * It only reports the choice; whoever uses it decides what it does.\n */\n@Component({\n selector: \"role-menu\",\n imports: [TranslocoPipe, ButtonComponent, ChevronDownIcon, EyeIcon, KeyIcon, MenuComponent, MenuContentComponent, MenuItemComponent, MenuTriggerDirective],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"inline-flex\" },\n template: `\n <button type=\"button\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [menuTrigger]=\"menu\" [disabled]=\"disabled()\" [attr.aria-label]=\"label()\">\n @if (role() === \"owner\") {\n <key-icon />\n } @else {\n <eye-icon />\n }\n {{ roleKey() | transloco }}\n <chevron-down-icon class=\"size-3\" />\n </button>\n <Menu #menu=\"menu\" placement=\"bottom-end\" (itemSelected)=\"onSelect($event)\">\n <MenuContent>\n <MenuItem value=\"owner\">\n <key-icon />\n {{ \"documentWorkspaces.share.owner\" | transloco }}\n </MenuItem>\n <MenuItem value=\"reader\">\n <eye-icon />\n {{ \"documentWorkspaces.share.reader\" | transloco }}\n </MenuItem>\n @if (removable()) {\n <MenuItem value=\"remove\" class=\"text-(--font-error-base)\">{{ \"documentWorkspaces.common.remove\" | transloco }}</MenuItem>\n }\n </MenuContent>\n </Menu>\n `\n})\nexport class RoleMenu {\n readonly role = input.required<AssignableRole>();\n /** Adds \"Remove\" to the menu. */\n readonly removable = input(false);\n readonly disabled = input(false);\n /** The accessible name of the button, already translated. */\n readonly label = input.required<string>();\n\n readonly roleChange = output<AssignableRole>();\n readonly removeRequested = output<void>();\n\n protected readonly roleKey = computed(() => (this.role() === \"owner\" ? \"documentWorkspaces.share.owner\" : \"documentWorkspaces.share.reader\"));\n\n protected onSelect(value: string): void {\n if (value === \"remove\") this.removeRequested.emit();\n else this.roleChange.emit(value as AssignableRole);\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output, signal } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, EyeIcon, KeyIcon } from \"@sinequa/galactik\";\nimport type { AssignableRole, Member } from \"../../models/share.model\";\nimport { PrincipalAvatar } from \"./principal-avatar\";\nimport { RoleMenu } from \"./role-menu\";\n\n/**\n * One person of the access list.\n *\n * Two rows never get a role menu: the creator's (immutable for the API) and your own\n * (an owner who demotes or removes themselves loses the workspace with no way back).\n * Removing asks first, inline in the row.\n */\n@Component({\n selector: \"member-row\",\n imports: [TranslocoPipe, ButtonComponent, EyeIcon, KeyIcon, PrincipalAvatar, RoleMenu],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"flex items-center gap-3 py-2\", role: \"listitem\" },\n template: `\n @if (confirming()) {\n <span class=\"min-w-0 flex-1 text-sm\">{{ \"documentWorkspaces.share.confirmRemove\" | transloco: { name: member().name } }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"confirming.set(false)\">{{ \"documentWorkspaces.common.cancel\" | transloco }}</button>\n <button size=\"sm\" class=\"bg-(--bg-error-base) enabled:hover:bg-(--bg-error-base) enabled:hover:opacity-90\" [disabled]=\"disabled()\" (click)=\"removed.emit()\">{{ \"documentWorkspaces.common.remove\" | transloco }}</button>\n } @else {\n <principal-avatar [name]=\"member().name\" [group]=\"member().isGroup\" />\n <div class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate text-sm\" [class.text-(--font-primary-muted)]=\"!member().resolved\" [attr.title]=\"member().resolved ? null : ('documentWorkspaces.share.unresolvedName' | transloco)\">\n {{ member().name }}\n @if (member().isSelf) {\n <span class=\"text-(--font-primary-muted)\">{{ \"documentWorkspaces.share.you\" | transloco }}</span>\n }\n </span>\n @if (member().email) {\n <span class=\"text-(--font-primary-muted) truncate text-xs\">{{ member().email }}</span>\n }\n </div>\n\n @if (member().role === \"creator\" || member().isSelf) {\n <span class=\"text-(--font-primary-muted) inline-flex items-center gap-1 text-sm\">\n @if (member().role === \"reader\") {\n <eye-icon class=\"size-4\" />\n {{ \"documentWorkspaces.share.reader\" | transloco }}\n } @else {\n <key-icon class=\"size-4\" />\n {{ \"documentWorkspaces.share.owner\" | transloco }}\n }\n </span>\n } @else {\n <role-menu\n [role]=\"assignable()\"\n [removable]=\"true\"\n [disabled]=\"disabled()\"\n [label]=\"'documentWorkspaces.share.changeRole' | transloco: { name: member().name }\"\n (roleChange)=\"roleChange.emit($event)\"\n (removeRequested)=\"confirming.set(true)\" />\n }\n }\n `\n})\nexport class MemberRow {\n readonly member = input.required<Member>();\n /** A change is in flight: nothing else can be asked meanwhile. */\n readonly disabled = input(false);\n\n readonly roleChange = output<AssignableRole>();\n readonly removed = output<void>();\n\n protected readonly confirming = signal(false);\n\n protected readonly assignable = computed<AssignableRole>(() => (this.member().role === \"reader\" ? \"reader\" : \"owner\"));\n}\n","import { ChangeDetectionStrategy, Component, inject, input, model, signal } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport type { SecurityPrincipal } from \"@sinequa/atomic\";\nimport { AutocompleteComponent, TagComponent, TagRemoveComponent } from \"@sinequa/galactik\";\nimport type { AssignableRole, PickedPrincipal } from \"../../models/share.model\";\nimport { PrincipalDirectory, principalDisplayName } from \"../../services/principal-directory.service\";\nimport { PrincipalAvatar } from \"./principal-avatar\";\nimport { RoleMenu } from \"./role-menu\";\n\n/**\n * Picks people: a tag-style box with search-as-you-type over the directory, a chip\n * per person, and the role the next ones get. The picked people are the `entries`\n * model — a parent owns them and can clear them.\n *\n * Groups never show up in the results: the API documents `owners`/`readers` as\n * *user* ids, so staging a group would look like it worked and grant nobody anything.\n *\n * The id of an entry is the principal's `userId` (`domain|id`), which is what a\n * workspace holds and the only form the API accepts.\n *\n * The search, the results popup (a combobox in the top layer, so a dialog's edges cannot clip it) and its keyboard\n * are those of the library's `Autocomplete`: this component adds what is specific to people — the avatar in a row, the\n * role of a chip, the role the next ones get. Like in a chat's mention box, Enter picks the first row and closes the\n * list.\n */\n@Component({\n selector: \"principal-picker\",\n imports: [TranslocoPipe, AutocompleteComponent, PrincipalAvatar, RoleMenu, TagComponent, TagRemoveComponent],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block\" },\n template: `\n <Autocomplete\n [selected]=\"entries()\"\n [loader]=\"search\"\n [key]=\"principalKey\"\n [label]=\"displayName\"\n [selectedKey]=\"entryKey\"\n [selectedLabel]=\"entryLabel\"\n [excludeKeys]=\"excludeIds()\"\n [indicator]=\"false\"\n [closeOnPick]=\"true\"\n [placeholder]=\"'documentWorkspaces.share.searchPlaceholder' | transloco\"\n [ariaLabel]=\"'documentWorkspaces.share.searchUsers' | transloco\"\n [noResultsText]=\"'documentWorkspaces.share.noMatches' | transloco\"\n [searchingText]=\"'documentWorkspaces.share.searching' | transloco\"\n [errorText]=\"'documentWorkspaces.share.searchFailed' | transloco\"\n (picked)=\"pick($event)\"\n (removed)=\"removeEntry($event)\">\n <ng-template #autocompleteOption let-person>\n <principal-avatar [name]=\"displayName(person)\" />\n <span class=\"flex min-w-0 flex-col\">\n <span class=\"text-(--font-primary-base) truncate text-sm font-medium\">{{ displayName(person) }}</span>\n <span class=\"text-(--font-primary-muted) truncate text-xs\">{{ person.longName }}</span>\n </span>\n </ng-template>\n\n <ng-template #autocompleteTag let-entry let-i=\"index\">\n <tag scheme=\"grey\" size=\"xs\" data-testid=\"chip\">\n {{ entry.name }}\n @if (showChipRole()) {\n <!-- A toggle rather than a menu: with two roles to choose from, a menu costs a click to say what one button already says. -->\n <button\n type=\"button\"\n class=\"text-(--font-primary-base) underline\"\n [attr.aria-label]=\"(entry.role === 'owner' ? 'documentWorkspaces.share.changeRoleToReader' : 'documentWorkspaces.share.changeRoleToOwner') | transloco: { name: entry.name }\"\n (click)=\"toggleRole(i)\">\n {{ (entry.role === \"owner\" ? \"documentWorkspaces.share.owner\" : \"documentWorkspaces.share.reader\") | transloco }}\n </button>\n }\n <tag-remove [aria-label]=\"'documentWorkspaces.share.removeUser' | transloco: { name: entry.name }\" (removed)=\"removeEntry(entry)\" />\n </tag>\n </ng-template>\n\n <role-menu\n autocompleteSuffix\n [role]=\"role()\"\n [label]=\"'documentWorkspaces.share.roleLabel' | transloco\"\n (roleChange)=\"role.set($event)\" />\n </Autocomplete>\n `\n})\nexport class PrincipalPicker {\n /** The people picked so far. */\n readonly entries = model<PickedPrincipal[]>([]);\n /** Ids to keep out of the results — the people who already have access. */\n readonly excludeIds = input<readonly string[]>([]);\n /** Chips carry a toggle to switch their role. Off where everyone picked takes the selector's role. */\n readonly showChipRole = input(false);\n\n private readonly directory = inject(PrincipalDirectory);\n\n /** The role the next person picked gets. */\n protected readonly role = signal<AssignableRole>(\"reader\");\n\n /** Groups are not offered: see the class comment. */\n protected readonly search = (query: string, { signal }: { signal: AbortSignal }) =>\n this.directory.search(query, signal).then(principals => principals.filter(p => !p.isGroup));\n\n protected readonly principalKey = (person: SecurityPrincipal) => person.userId;\n protected readonly entryKey = (entry: PickedPrincipal) => entry.id;\n protected readonly entryLabel = (entry: PickedPrincipal) => entry.name;\n protected readonly displayName = (person: SecurityPrincipal) => principalDisplayName(person);\n\n protected pick(person: SecurityPrincipal): void {\n if (this.entries().some(e => e.id === person.userId)) return;\n this.entries.update(entries => [...entries, { id: person.userId, name: principalDisplayName(person), isGroup: person.isGroup, role: this.role() }]);\n }\n\n protected toggleRole(index: number): void {\n this.entries.update(entries => entries.map((e, i) => (i === index ? { ...e, role: e.role === \"owner\" ? \"reader\" : \"owner\" } : e)));\n }\n\n protected removeEntry(entry: PickedPrincipal): void {\n this.entries.update(entries => entries.filter(e => e.id !== entry.id));\n }\n}\n","import { afterNextRender, ChangeDetectionStrategy, Component, computed, inject, signal, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n createCallable,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogResult,\n DialogTitleComponent,\n injectCallRef\n} from \"@sinequa/galactik\";\nimport type { AssignableRole, PickedPrincipal } from \"../../models/share.model\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { CurrentUserService } from \"../../services/current-user.service\";\nimport { PrincipalDirectory } from \"../../services/principal-directory.service\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { workspaceErrorMessage, WorkspaceErrorMessage } from \"../../utils/error-message\";\nimport { buildMembers, withAdded, withRole } from \"../../utils/members\";\nimport { MemberRow } from \"./member-row\";\nimport { PrincipalPicker } from \"./principal-picker\";\n\nexport interface ShareDialogConfig {\n workspaceId: Workspace[\"id\"];\n}\n\n/**\n * \"Manage access\": who can do what in a workspace.\n *\n * ```ts\n * await ShareWorkspace.call({ workspaceId });\n * ```\n *\n * Every change — a role, a removal, people added — is saved at once. The list reads\n * the workspace from `WorkspacesStore`, so it shows what the server now holds, and\n * stays in step with a change made elsewhere. Only the ones who can write to the\n * workspace get here; the server refuses the others anyway.\n */\n@Component({\n selector: \"share-dialog\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogTitleComponent,\n MemberRow,\n PrincipalPicker\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <dialog #dialog (closed)=\"call.end($event)\">\n <DialogContent size=\"md\">\n <DialogHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\">\n <DialogTitle>{{ \"documentWorkspaces.share.title\" | transloco }}</DialogTitle>\n </DialogHeader>\n <DialogBody>\n @if (workspace(); as workspace) {\n <p class=\"text-(--font-primary-muted) mb-3 text-sm\">{{ workspace.name }}</p>\n\n <div class=\"flex items-start gap-2\">\n <principal-picker class=\"flex-1\" [(entries)]=\"staged\" [excludeIds]=\"memberIds()\" />\n <button [disabled]=\"!staged().length || busy()\" (click)=\"add()\">\n {{ (adding() ? \"documentWorkspaces.share.adding\" : \"documentWorkspaces.share.addPeople\") | transloco }}\n </button>\n </div>\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">\n @if (\"key\" in message) {\n {{ message.key | transloco }}\n } @else {\n {{ message.text }}\n }\n </p>\n }\n\n <p class=\"text-(--font-primary-muted) text-xs\" aria-live=\"polite\">{{ \"documentWorkspaces.share.accessCount\" | transloco: { count: members().length } }}</p>\n <div role=\"list\" class=\"divide-(--stroke-primary-lighter) max-h-72 divide-y overflow-auto\" data-testid=\"members\">\n @for (member of members(); track member.id) {\n <member-row [member]=\"member\" [disabled]=\"busy()\" (roleChange)=\"change(member.id, $event)\" (removed)=\"change(member.id, 'remove')\" />\n }\n </div>\n\n <p class=\"text-(--font-primary-muted) text-xs\">{{ \"documentWorkspaces.share.accessNotice\" | transloco }}</p>\n }\n </DialogBody>\n\n <DialogFooter>\n <button variant=\"secondary\" (click)=\"dialog.close('dialog-close')\">{{ \"documentWorkspaces.common.close\" | transloco }}</button>\n </DialogFooter>\n </DialogContent>\n </dialog>\n `\n})\nexport class ShareDialog {\n protected readonly call = injectCallRef<ShareDialogConfig, DialogResult>();\n\n private readonly dialog = viewChild.required(DialogComponent);\n private readonly store = inject(WorkspacesStore);\n private readonly directory = inject(PrincipalDirectory);\n // Read so the list re-renders once the logged-in user is known: it decides whose row has no role menu.\n private readonly currentUser = inject(CurrentUserService);\n\n private readonly workspaceId = computed(() => this.call.props().workspaceId);\n protected readonly workspace = computed(() => this.store.workspace(this.workspaceId()));\n protected readonly members = computed(() => {\n this.currentUser.user();\n const workspace = this.workspace();\n return workspace ? buildMembers(workspace, id => this.directory.resolve(id)) : [];\n });\n protected readonly memberIds = computed(() => this.members().map(m => m.id));\n\n /** The people picked, not added yet. */\n protected readonly staged = signal<PickedPrincipal[]>([]);\n protected readonly adding = signal(false);\n protected readonly busy = signal(false);\n protected readonly error = signal<WorkspaceErrorMessage | null>(null);\n\n constructor() {\n afterNextRender(() => {\n this.dialog().showModal();\n // Names arrive after the first paint; the list shows raw ids until then.\n void this.directory.ensureResolved(this.memberIds());\n });\n }\n\n protected async add(): Promise<void> {\n const workspace = this.workspace();\n const picked = this.staged();\n if (!workspace || !picked.length) return;\n this.adding.set(true);\n await this.save(\n () => withAdded(workspace, picked),\n () => this.staged.set([])\n );\n this.adding.set(false);\n }\n\n protected async change(id: string, role: AssignableRole | \"remove\"): Promise<void> {\n const workspace = this.workspace();\n if (!workspace) return;\n await this.save(() => withRole(workspace, id, role));\n }\n\n /** One write at a time: the arrays it sends are built from the workspace as it is now. */\n private async save(access: () => { owners: string[]; readers: string[] }, onSaved?: () => void): Promise<void> {\n if (this.busy()) return;\n this.busy.set(true);\n this.error.set(null);\n try {\n const updated = await this.store.update(this.workspaceId(), access());\n if (!updated) {\n this.error.set({ key: \"documentWorkspaces.errors.notFound\" });\n return;\n }\n onSaved?.();\n } catch (error) {\n this.error.set(workspaceErrorMessage(error));\n } finally {\n this.busy.set(false);\n }\n }\n}\n\n/** Imperative entry point: `await ShareWorkspace.call({ workspaceId })`. */\nexport const ShareWorkspace = createCallable<ShareDialogConfig, DialogResult>(ShareDialog);\n","import { ChangeDetectionStrategy, Component, computed, effect, inject, input, output, signal, untracked, viewChild, ElementRef } from \"@angular/core\";\nimport { toSignal } from \"@angular/core/rxjs-interop\";\nimport { disabled, form, FormField } from \"@angular/forms/signals\";\nimport { DomSanitizer, SafeResourceUrl } from \"@angular/platform-browser\";\nimport { provideTranslocoScope, TranslocoPipe, TranslocoService } from \"@jsverse/transloco\";\nimport { fetchPreview } from \"@sinequa/atomic\";\nimport {\n ButtonComponent,\n ChatPlusIcon,\n ChevronDownIcon,\n ChevronUpIcon,\n ExternalLinkIcon,\n MagnifyingGlassMinusIcon,\n MagnifyingGlassPlusIcon,\n SearchInputComponent,\n XMarkIcon,\n TooltipDirective\n} from \"@sinequa/galactik\";\nimport { injectTextSearch } from \"../../composables/inject-text-search\";\nimport type { WorkspaceDocument } from \"../../models/workspace.model\";\nimport { resolveEndpointUrl } from \"../../utils/base-url\";\nimport { formatDate } from \"../../utils/format\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\n\n/** What a search app falls back to when the app config names no query. */\nconst DEFAULT_QUERY_NAME = \"_query\";\n\nconst ZOOM_STEP = 0.1;\nconst ZOOM_MIN = 0.25;\nconst ZOOM_MAX = 2.5;\nconst ZOOM_DEFAULT = 0.75;\n\n/** What \"Ask follow-up\" hands the host: what to show in its compose area, already translated. */\nexport interface AskFollowUp {\n title: string;\n type: WorkspaceDocument[\"type\"];\n placeholder: string;\n suggestions: string[];\n}\n\n/**\n * The preview of a document: the converted copy the preview web service points at,\n * in a frame, with a zoom, a find-in-text and an \"Ask follow-up\".\n *\n * It asks the service for the document by id, as a search app does. Until the\n * backend hands out the index id for an uploaded document (the listing carries\n * the upload-side id) the service answers an error, and the panel says the document\n * cannot be previewed rather than showing a stand-in.\n *\n * Finding text needs to read the frame, so it only works when the copy is served\n * from the same origin as the page; otherwise the box is disabled and says why.\n */\n@Component({\n selector: \"document-preview-panel\",\n imports: [\n TranslocoPipe,\n FormField,\n ButtonComponent,\n ChatPlusIcon,\n ChevronDownIcon,\n ChevronUpIcon,\n DocumentTypeIcon,\n ExternalLinkIcon,\n MagnifyingGlassMinusIcon,\n MagnifyingGlassPlusIcon,\n SearchInputComponent,\n TooltipDirective,\n XMarkIcon\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"flex min-h-0 flex-col bg-(--bg-neutral-white)\" },\n template: `\n <header class=\"flex flex-col gap-3 border-b border-(--stroke-primary-lighter) p-4\">\n <div class=\"flex items-start gap-3\">\n <document-type-icon size=\"md\" [type]=\"document().type\" />\n <h2 class=\"min-w-0 flex-1 pt-1.5 text-base leading-[22px] font-semibold text-(--font-neutral-black)\">\n @if (titleUrl(); as url) {\n <a class=\"inline-flex max-w-full items-center gap-1 hover:underline\" [href]=\"url\" target=\"_blank\" rel=\"noopener\">\n <span class=\"truncate\">{{ document().name }}</span>\n <external-link-icon class=\"size-3.5 shrink-0\" />\n </a>\n } @else {\n <span class=\"block truncate\" data-testid=\"title\">{{ document().name }}</span>\n }\n </h2>\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'documentWorkspaces.preview.close' | transloco\"\n [tooltip]=\"'documentWorkspaces.preview.close' | transloco\"\n (click)=\"closed.emit()\">\n <x-mark-icon />\n </button>\n </div>\n\n <dl class=\"flex flex-col gap-1 text-xs text-(--font-primary-base)\">\n @if (date(); as date) {\n <div class=\"flex gap-1\" data-testid=\"date\">\n <dt class=\"font-semibold\">{{ \"documentWorkspaces.preview.dateLabel\" | transloco }}</dt>\n <dd>{{ date }}</dd>\n </div>\n }\n @if (document().source) {\n <div class=\"flex gap-1\">\n <dt class=\"font-semibold\">{{ \"documentWorkspaces.preview.sourceLabel\" | transloco }}</dt>\n <dd>{{ document().source }}</dd>\n </div>\n }\n </dl>\n\n <div class=\"flex flex-wrap items-center gap-2\">\n <SearchInput\n role=\"search\"\n class=\"min-w-40 flex-[1_1_12rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.preview.findPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.preview.findPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\"\n [attr.title]=\"searchable() ? null : ('documentWorkspaces.preview.searchUnavailable' | transloco)\"\n (keydown)=\"onSearchKeydown($event)\">\n @if (query()) {\n <span class=\"shrink-0 text-xs text-(--font-primary-muted)\" aria-live=\"polite\" data-testid=\"count\">\n @if (search.matches()) {\n {{ \"documentWorkspaces.preview.searchCount\" | transloco: { current: search.activeIndex() + 1, total: search.matches() } }}\n } @else {\n {{ \"documentWorkspaces.preview.noResults\" | transloco }}\n }\n </span>\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"!search.matches()\"\n [attr.aria-label]=\"'documentWorkspaces.preview.previousResult' | transloco\"\n (click)=\"search.previous()\">\n <chevron-up-icon />\n </button>\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"!search.matches()\"\n [attr.aria-label]=\"'documentWorkspaces.preview.nextResult' | transloco\"\n (click)=\"search.next()\">\n <chevron-down-icon />\n </button>\n }\n </SearchInput>\n\n <button variant=\"secondary\" size=\"sm\" (click)=\"askFollowUpClicked()\">\n <chat-plus-icon />\n {{ \"documentWorkspaces.preview.askFollowUp\" | transloco }}\n </button>\n\n <div class=\"flex items-center\">\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"zoom() <= zoomMin\"\n [attr.aria-label]=\"'documentWorkspaces.preview.zoomOut' | transloco\"\n [tooltip]=\"'documentWorkspaces.preview.zoomOut' | transloco\"\n (click)=\"zoomBy(-1)\">\n <magnifying-glass-minus-icon />\n </button>\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"zoom() >= zoomMax\"\n [attr.aria-label]=\"'documentWorkspaces.preview.zoomIn' | transloco\"\n [tooltip]=\"'documentWorkspaces.preview.zoomIn' | transloco\"\n (click)=\"zoomBy(1)\">\n <magnifying-glass-plus-icon />\n </button>\n </div>\n </div>\n </header>\n\n <div class=\"relative min-h-0 flex-1 bg-(--bg-primary-lightest)\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"p-6 text-center text-sm text-(--font-primary-base)\" data-testid=\"loading\">{{ \"documentWorkspaces.preview.loading\" | transloco }}</p>\n }\n @case (\"unavailable\") {\n <p class=\"p-6 text-center text-sm text-(--font-primary-base)\" data-testid=\"unavailable\">{{ \"documentWorkspaces.preview.unavailable\" | transloco }}</p>\n }\n @case (\"ready\") {\n <!-- The frame is scaled, and its box stretched by the inverse so that, scaled, it still fills the panel. -->\n <div class=\"absolute inset-0 overflow-hidden\">\n <div class=\"origin-top-left\" [style.transform]=\"'scale(' + zoom() + ')'\" [style.width.%]=\"100 / zoom()\" [style.height.%]=\"100 / zoom()\">\n <iframe #frame class=\"size-full border-0 bg-white\" [title]=\"document().name\" [src]=\"frameUrl()\" (load)=\"onFrameLoad()\"></iframe>\n </div>\n </div>\n }\n }\n </div>\n `\n})\nexport class DocumentPreviewPanel {\n readonly document = input.required<WorkspaceDocument>();\n /** The query the preview web service renders the document for — the host's `query-name`. */\n readonly queryName = input(DEFAULT_QUERY_NAME);\n\n readonly closed = output<void>();\n readonly askFollowUp = output<AskFollowUp>();\n\n private readonly sanitizer = inject(DomSanitizer);\n private readonly transloco = inject(TranslocoService);\n private readonly lang = toSignal(this.transloco.langChanges$, { initialValue: this.transloco.getActiveLang() });\n private readonly frame = viewChild<ElementRef<HTMLIFrameElement>>(\"frame\");\n\n protected readonly zoomMin = ZOOM_MIN;\n protected readonly zoomMax = ZOOM_MAX;\n protected readonly zoom = signal(ZOOM_DEFAULT);\n protected readonly status = signal<\"loading\" | \"ready\" | \"unavailable\">(\"loading\");\n protected readonly frameUrl = signal<SafeResourceUrl | null>(null);\n protected readonly titleUrl = signal<string | undefined>(undefined);\n protected readonly date = computed(() => formatDate(this.document().createdAt, this.lang()));\n\n /** The text of the frame, once it has loaded and the page is allowed to read it. */\n private readonly frameBody = signal<Node | null>(null);\n protected readonly searchable = computed(() => this.frameBody() !== null);\n protected readonly query = signal(\"\");\n /** The find box, bound to `query`; off while the frame cannot be read. */\n protected readonly searchForm = form(this.query, path => disabled(path, () => !this.searchable()));\n protected readonly search = injectTextSearch({ root: this.frameBody, query: this.query });\n\n /** A slow answer for a document the user has already left must not replace the one on screen. */\n private token = 0;\n\n constructor() {\n effect(() => {\n const { id } = this.document();\n const queryName = this.queryName();\n untracked(() => void this.load(id, queryName));\n });\n }\n\n private async load(id: string, queryName: string): Promise<void> {\n const token = ++this.token;\n this.status.set(\"loading\");\n this.frameUrl.set(null);\n this.titleUrl.set(undefined);\n this.frameBody.set(null);\n this.query.set(\"\");\n\n try {\n const data = await fetchPreview(id, { name: queryName, text: \"\" });\n if (token !== this.token) return;\n if (!data.documentCachedContentUrl) {\n this.status.set(\"unavailable\");\n return;\n }\n // The URL is the preview service's own, resolved under the configured backend: trusted as a frame source.\n this.frameUrl.set(this.sanitizer.bypassSecurityTrustResourceUrl(resolveEndpointUrl(data.documentCachedContentUrl).href));\n this.titleUrl.set(data.record?.url1 || data.record?.originalUrl || undefined);\n this.status.set(\"ready\");\n } catch {\n // Expected until the backend exposes the index id of an uploaded document: say so, rather than fake one.\n if (token === this.token) this.status.set(\"unavailable\");\n }\n }\n\n protected onFrameLoad(): void {\n try {\n this.frameBody.set(this.frame()?.nativeElement.contentDocument?.body ?? null);\n } catch {\n // A copy served from another origin cannot be read: there is nothing to search.\n this.frameBody.set(null);\n }\n }\n\n protected zoomBy(direction: 1 | -1): void {\n this.zoom.update(zoom => Math.round(Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoom + direction * ZOOM_STEP)) * 100) / 100);\n }\n\n protected onSearchKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n if (event.shiftKey) this.search.previous();\n else this.search.next();\n } else if (event.key === \"Escape\" && this.query()) {\n // Clears the box; with nothing typed, Escape is the host's.\n event.stopPropagation();\n this.query.set(\"\");\n }\n }\n\n protected askFollowUpClicked(): void {\n const t = (key: string) => this.transloco.translate(`documentWorkspaces.preview.${key}`);\n this.askFollowUp.emit({\n title: this.document().name,\n type: this.document().type,\n placeholder: t(\"askFollowUpPlaceholder\"),\n suggestions: [t(\"suggestionSummarize\"), t(\"suggestionKeyPoints\"), t(\"suggestionActionItems\")]\n });\n }\n}\n","import { afterNextRender, ChangeDetectionStrategy, Component, computed, inject, signal, viewChild } from \"@angular/core\";\nimport { FieldTree, form, FormField, FormRoot, maxLength, required, validate } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n createCallable,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogResult,\n DialogTitleComponent,\n injectCallRef,\n InputComponent,\n InputControlDirective,\n TagInputComponent,\n TextareaComponent,\n TextareaControlDirective,\n TextareaCounterComponent\n} from \"@sinequa/galactik\";\nimport type { PickedPrincipal } from \"../../models/share.model\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { CurrentUserService } from \"../../services/current-user.service\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { workspaceErrorMessage, WorkspaceErrorMessage } from \"../../utils/error-message\";\nimport { PrincipalPicker } from \"../share/principal-picker\";\n\nexport const TITLE_MAX_LENGTH = 120;\nexport const DESCRIPTION_MAX_LENGTH = 300;\n\nexport type WorkspaceEditDialogConfig = { mode: \"add\" } | { mode: \"edit\"; workspace: Workspace };\n\n/** What the dialog resolves with once it saved: the workspace as the server now has it. */\nexport type WorkspaceEditResult = { type: \"dialog-confirm\"; workspace: Workspace };\n\n/**\n * Creates a workspace, or edits the title, description and tags of one.\n *\n * ```ts\n * const result = await EditWorkspace.call({ mode: \"add\" });\n * if (typeof result === \"object\") open(result[\"workspace\"]);\n * ```\n *\n * It saves by itself — on failure it stays open with the error, so the user can\n * retry — and while a request is in flight it cannot be dismissed. Editing never\n * sends `owners`/`readers`: the API replaces both wholesale, so sending a snapshot\n * taken when the dialog opened could undo what \"Manage access\" did since.\n */\n@Component({\n selector: \"workspace-edit-dialog\",\n imports: [\n TranslocoPipe,\n FormField,\n FormRoot,\n ButtonComponent,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogTitleComponent,\n InputComponent,\n InputControlDirective,\n PrincipalPicker,\n TagInputComponent,\n TextareaComponent,\n TextareaControlDirective,\n TextareaCounterComponent\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <dialog #dialog (cancel)=\"onNativeCancel($event)\" (closed)=\"onClosed($event)\">\n <DialogContent size=\"md\">\n <!-- The header's close button cannot be disabled, so the whole header goes inert while the request runs. -->\n <DialogHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\" [attr.inert]=\"busy() ? '' : null\">\n <DialogTitle>{{ (adding() ? \"documentWorkspaces.edit.addTitle\" : \"documentWorkspaces.edit.editTitle\") | transloco }}</DialogTitle>\n </DialogHeader>\n\n <form [formRoot]=\"editForm\" class=\"contents\">\n <DialogBody class=\"gap-(--spacing-md)\">\n <div class=\"flex flex-col gap-(--spacing-2xs)\">\n <label for=\"workspace-edit-name\" class=\"text-sm font-medium\">{{ \"documentWorkspaces.edit.nameLabel\" | transloco }}</label>\n <input-group [scheme]=\"showError(editForm.name) ? 'error' : 'default'\">\n <input\n input-control\n id=\"workspace-edit-name\"\n type=\"text\"\n autocomplete=\"off\"\n autofocus\n [formField]=\"editForm.name\"\n [attr.aria-invalid]=\"showError(editForm.name) ? 'true' : null\" />\n </input-group>\n @if (showError(editForm.name)) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-xs\">{{ \"documentWorkspaces.edit.nameRequired\" | transloco }}</p>\n }\n </div>\n\n <div class=\"flex flex-col gap-(--spacing-2xs)\">\n <label for=\"workspace-edit-description\" class=\"text-sm font-medium\">{{ \"documentWorkspaces.edit.descriptionLabel\" | transloco }}</label>\n <textarea-group [scheme]=\"descriptionTooLong() ? 'error' : 'default'\" resize=\"none\">\n <textarea\n textarea-control\n id=\"workspace-edit-description\"\n rows=\"3\"\n [formField]=\"editForm.description\"\n [attr.placeholder]=\"'documentWorkspaces.edit.descriptionPlaceholder' | transloco\"\n [attr.aria-invalid]=\"descriptionTooLong() ? 'true' : null\"></textarea>\n <textarea-counter [scheme]=\"descriptionTooLong() ? 'error' : 'default'\" data-testid=\"counter\">{{ description().length }}/{{ descriptionMax }}</textarea-counter>\n </textarea-group>\n </div>\n\n <div class=\"flex flex-col gap-(--spacing-2xs)\">\n <span class=\"text-sm font-medium\">{{ \"documentWorkspaces.edit.tagsLabel\" | transloco }}</span>\n <TagInput\n [formField]=\"editForm.tags\"\n [aria-label]=\"'documentWorkspaces.edit.tagsLabel' | transloco\"\n [placeholder]=\"'documentWorkspaces.edit.tagsPlaceholder' | transloco\"\n [addLabel]=\"'documentWorkspaces.edit.addTag' | transloco\"\n [removeLabel]=\"'documentWorkspaces.edit.removeTag' | transloco: { tag: '{tag}' }\"\n [addedLabel]=\"'documentWorkspaces.edit.tagAdded' | transloco: { tag: '{tag}' }\"\n [removedLabel]=\"'documentWorkspaces.edit.tagRemoved' | transloco: { tag: '{tag}' }\" />\n <span class=\"text-(--font-primary-muted) text-xs\">{{ \"documentWorkspaces.edit.tagsHint\" | transloco }}</span>\n </div>\n\n @if (adding()) {\n <!-- Only when creating: an existing workspace has \"Manage access\", and two places writing the same two arrays is one too many. -->\n <div class=\"flex flex-col gap-(--spacing-2xs)\">\n <span class=\"text-sm font-medium\">{{ \"documentWorkspaces.edit.shareLabel\" | transloco }}</span>\n <principal-picker [(entries)]=\"sharedWith\" [showChipRole]=\"true\" [excludeIds]=\"excludedIds()\" />\n <span class=\"text-(--font-primary-muted) text-xs\">{{ \"documentWorkspaces.edit.shareHint\" | transloco }}</span>\n </div>\n }\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">\n @if (\"key\" in message) {\n {{ message.key | transloco }}\n } @else {\n {{ message.text }}\n }\n </p>\n }\n </DialogBody>\n\n <DialogFooter>\n <button type=\"button\" variant=\"secondary\" [disabled]=\"busy()\" (click)=\"dialog.close('dialog-cancel')\">\n {{ \"documentWorkspaces.common.cancel\" | transloco }}\n </button>\n <button type=\"submit\" [disabled]=\"busy() || descriptionTooLong()\">\n {{ saveLabel() | transloco }}\n </button>\n </DialogFooter>\n </form>\n </DialogContent>\n </dialog>\n `\n})\nexport class WorkspaceEditDialog {\n protected readonly call = injectCallRef<WorkspaceEditDialogConfig, DialogResult>();\n\n private readonly dialog = viewChild.required(DialogComponent);\n private readonly store = inject(WorkspacesStore);\n private readonly currentUser = inject(CurrentUserService);\n\n protected readonly titleMax = TITLE_MAX_LENGTH;\n protected readonly descriptionMax = DESCRIPTION_MAX_LENGTH;\n\n private readonly config = this.call.props();\n protected readonly adding = computed(() => this.config.mode === \"add\");\n private readonly initial = this.config.mode === \"edit\" ? this.config.workspace : null;\n\n private readonly model = signal({\n name: this.initial?.name ?? \"\",\n description: this.initial?.description ?? \"\",\n tags: this.initial ? [...this.initial.tags] : []\n });\n private readonly tagInput = viewChild.required(TagInputComponent);\n\n protected readonly editForm = form(\n this.model,\n path => {\n required(path.name);\n // `required` lets whitespace through: a title of spaces is no title.\n validate(path.name, ({ value }) => (value().trim() ? undefined : { kind: \"required\", message: \"required\" }));\n maxLength(path.name, TITLE_MAX_LENGTH);\n maxLength(path.description, DESCRIPTION_MAX_LENGTH);\n },\n { submission: { action: async () => this.save() } }\n );\n\n protected readonly description = computed(() => this.model().description);\n /** The people picked to share a new workspace with. */\n protected readonly sharedWith = signal<PickedPrincipal[]>([]);\n /** Never offered: they are the creator-to-be, and go into `owners` when the workspace is created. */\n protected readonly excludedIds = computed(() => [this.currentUser.user()?.id].filter((id): id is string => !!id));\n protected readonly busy = signal(false);\n protected readonly error = signal<WorkspaceErrorMessage | null>(null);\n\n protected readonly descriptionTooLong = computed(() => this.model().description.length > DESCRIPTION_MAX_LENGTH);\n protected readonly saveLabel = computed(() => {\n if (this.busy()) return this.adding() ? \"documentWorkspaces.edit.creating\" : \"documentWorkspaces.edit.saving\";\n return this.adding() ? \"documentWorkspaces.common.create\" : \"documentWorkspaces.common.save\";\n });\n\n /** What to resolve with once the native dialog has closed: the saved workspace, or the plain dismissal. */\n private outcome: DialogResult | null = null;\n\n constructor() {\n afterNextRender(() => this.dialog().showModal());\n }\n\n /** A field shows its error only once the user has left it, or tried to submit. */\n protected showError<T>(field: FieldTree<T>): boolean {\n return field().touched() && field().invalid();\n }\n\n /** The submission itself, only ever reached with a valid form: the title is there, the description fits. */\n private async save(): Promise<void> {\n if (this.busy()) return;\n\n // What is typed in the tag box and not committed yet still counts.\n this.tagInput().commit();\n\n const { name, description, tags } = this.model();\n this.busy.set(true);\n this.error.set(null);\n try {\n let workspace: Workspace | null;\n if (this.config.mode === \"add\") {\n // The creator goes into `owners` as well as `creator`. Awaited rather than read\n // off the signal: a create fired before the principal request settled would\n // otherwise drop the id silently, and nothing shows the array is short.\n const creator = await this.currentUser.whenReady();\n const shared = this.sharedWith();\n const owners = shared.filter(p => p.role === \"owner\").map(p => p.id);\n workspace = await this.store.create({\n name: name.trim(),\n description: description.trim(),\n tags,\n owners: creator && !owners.includes(creator.id) ? [...owners, creator.id] : owners,\n readers: shared.filter(p => p.role === \"reader\").map(p => p.id)\n });\n } else {\n workspace = await this.store.update(this.config.workspace.id, { name: name.trim(), description: description.trim(), tags });\n }\n if (!workspace) {\n this.error.set({ key: \"documentWorkspaces.errors.notFound\" });\n return;\n }\n this.outcome = { type: \"dialog-confirm\", workspace } satisfies WorkspaceEditResult;\n } catch (error) {\n this.error.set(workspaceErrorMessage(error));\n return;\n } finally {\n this.busy.set(false);\n }\n this.dialog().close(\"dialog-confirm\");\n }\n\n protected onClosed(event: DialogResult): void {\n this.call.end(this.outcome ?? event);\n }\n\n /** Escape on a native <dialog> closes it: refuse while the request is running. */\n protected onNativeCancel(event: Event): void {\n if (this.busy()) event.preventDefault();\n }\n}\n\n/** Imperative entry point: `await EditWorkspace.call({ mode: \"add\" })`. */\nexport const EditWorkspace = createCallable<WorkspaceEditDialogConfig, DialogResult>(WorkspaceEditDialog);\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n effect,\n ElementRef,\n inject,\n Injector,\n input,\n model,\n output,\n signal,\n untracked\n} from \"@angular/core\";\nimport { form, FormField } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe, TranslocoService } from \"@jsverse/transloco\";\nimport { ApiError } from \"@sinequa/atomic\";\nimport {\n ButtonComponent,\n CheckboxComponent,\n ChevronLeftIcon,\n FileSearchIcon,\n GridIcon,\n ListIcon,\n PenToSquareIcon,\n RefreshIcon,\n SearchInputComponent,\n TagIcon,\n TrashIcon,\n UploadIcon,\n UserRoundIcon,\n TooltipDirective\n} from \"@sinequa/galactik\";\nimport { injectLiveRefresh } from \"../../composables/inject-live-refresh\";\nimport { injectSelection } from \"../../composables/inject-selection\";\nimport { ResizeHandle } from \"../../directives/resize-handle.directive\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { Workspace, WorkspaceDocument } from \"../../models/workspace.model\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { UploadService } from \"../../upload/upload.service\";\nimport { workspaceErrorMessage } from \"../../utils/error-message\";\nimport { Message } from \"../../utils/message\";\nimport { matchesText } from \"../../utils/text-match\";\nimport { canWrite } from \"../../utils/workspace-role\";\nimport { ConfirmAction, ConfirmDialogConfig } from \"../shared/confirm.dialog\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\nimport { UploadDocuments } from \"../upload/upload.dialog\";\nimport { DocumentCard } from \"./document-card\";\nimport { DOCUMENT_ROW_COLUMNS, DocumentRow } from \"./document-row\";\nimport { AskFollowUp, DocumentPreviewPanel } from \"../preview/document-preview-panel\";\nimport { ShareWorkspace } from \"../share/share.dialog\";\nimport { EditWorkspace, WorkspaceEditResult } from \"./workspace-edit.dialog\";\n\ntype ViewMode = \"grid\" | \"list\";\n\n/**\n * One workspace and its documents, as a grid or a list, with search, multi-selection,\n * upload, edit and delete.\n *\n * It reads `WorkspacesStore` for both the workspace and its documents, so a change\n * made elsewhere — a role changed from \"Manage access\", a status patched by the\n * upload flow — shows without anything being pushed in. It does not know where it is\n * shown: it says what the user asked for (`back`, `deleted`, `askFollowUp`) and whoever\n * hosts it decides. The document open in the preview is the `previewedDocumentId` model.\n *\n * Only someone who can write sees the edit, share, delete, upload and select controls.\n *\n * While a workspace stays open, it keeps polling its documents and its own\n * metadata in the background (`injectLiveRefresh`), so an edit or a document\n * another user adds shows up on its own.\n */\n@Component({\n selector: \"workspace-detail\",\n imports: [\n TranslocoPipe,\n FormField,\n ButtonComponent,\n CheckboxComponent,\n ChevronLeftIcon,\n DocumentCard,\n DocumentPreviewPanel,\n DocumentRow,\n FileSearchIcon,\n GridIcon,\n ListIcon,\n PenToSquareIcon,\n RefreshIcon,\n ResizeHandle,\n SearchInputComponent,\n TagIcon,\n TooltipDirective,\n TrashIcon,\n UploadIcon,\n UserRoundIcon,\n WorkspaceMessage,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"flex h-full min-h-0\" },\n template: `\n <div class=\"@container flex min-h-0 min-w-0 flex-1 flex-col p-3 pb-2 md:p-8 md:pb-4\">\n <div class=\"mb-3 flex flex-wrap items-center gap-x-3 gap-y-2\">\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'documentWorkspaces.detail.back' | transloco\"\n [tooltip]=\"'documentWorkspaces.detail.back' | transloco\"\n (click)=\"leave()\">\n <chevron-left-icon />\n </button>\n\n @if (workspace(); as workspace) {\n <h1 class=\"min-w-28 flex-[1_1_16rem] text-2xl leading-[31px] font-bold break-words text-(--font-neutral-black)\">{{ workspace.name }}</h1>\n\n <!-- Beside the title while they fit next to its 16rem, on a line of their own under it once they do not. -->\n @if (!deletedElsewhere()) {\n <div class=\"ml-auto flex shrink-0 items-center gap-1\">\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\"\n [writerOnly]=\"workspace.currentUserRole\"\n (click)=\"confirmDeleteWorkspace(workspace)\">\n <trash-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n <button size=\"sm\" variant=\"tertiary\" scheme=\"neutral\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"share(workspace)\">\n <user-round-icon />\n {{ \"documentWorkspaces.share.title\" | transloco }}\n </button>\n <button size=\"sm\" variant=\"tertiary\" scheme=\"neutral\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"edit(workspace)\">\n <pen-to-square-icon />\n {{ \"documentWorkspaces.common.edit\" | transloco }}\n </button>\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n [iconOnly]=\"true\"\n [disabled]=\"refreshing()\"\n [attr.aria-label]=\"'documentWorkspaces.common.refresh' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.refresh' | transloco\"\n (click)=\"refresh()\">\n <refresh-icon />\n </button>\n </div>\n }\n }\n </div>\n\n @if (workspace(); as workspace) {\n @if (workspace.description) {\n <p class=\"mb-3 text-base text-(--font-primary-base)\">{{ workspace.description }}</p>\n }\n @if (workspace.tags.length) {\n <p class=\"mb-6 flex items-center gap-2 text-sm text-(--font-primary-base)\" data-testid=\"tags\">\n <tag-icon class=\"size-4\" />\n {{ workspace.tags.join(\", \") }}\n </p>\n }\n\n @if (!deletedElsewhere()) {\n <div class=\"mb-3 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <SearchInput\n role=\"search\"\n class=\"min-w-32 flex-[0_1_15rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.detail.searchPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.detail.searchPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n\n <button class=\"@max-md:px-2\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"upload(workspace)\">\n <upload-icon />\n <span class=\"@max-md:sr-only\">{{ \"documentWorkspaces.detail.upload\" | transloco }}</span>\n </button>\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <trash-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n <p class=\"mb-6 text-sm text-(--font-primary-base)\" aria-live=\"polite\" data-testid=\"count\">\n @if (selection.active()) {\n {{ \"documentWorkspaces.detail.selectedDocumentCount\" | transloco: { count: selection.count() } }}\n } @else {\n {{ \"documentWorkspaces.detail.fileCount\" | transloco: { count: workspace.documentCount } }}\n }\n </p>\n }\n }\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">{{ \"documentWorkspaces.detail.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the documents scroll: the title, the toolbar and the count stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-(--font-primary-muted) text-sm\">{{ \"documentWorkspaces.detail.docsLoading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.detail.docsLoadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (deletedElsewhere()) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"not-found\">\n <div class=\"mb-1 inline-flex size-14 shrink-0 items-center justify-center rounded-(--radius-xl) bg-(--teal-100,#E0F4F1) text-(--teal-400,#4EBFB0)\">\n <file-search-icon class=\"size-6\" />\n </div>\n <p class=\"max-w-70 text-sm text-(--font-primary-base)\">{{ \"documentWorkspaces.errors.notFound\" | transloco }}</p>\n <button variant=\"secondary\" size=\"sm\" class=\"mt-2\" (click)=\"leave()\">{{ \"documentWorkspaces.detail.back\" | transloco }}</button>\n </div>\n } @else if (workspace(); as workspace) {\n @if (!documents().length) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"empty\">\n <svg class=\"mb-1 block size-14 shrink-0\" width=\"56\" height=\"56\" viewBox=\"0 0 38 38\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" data-testid=\"empty-icon\">\n <g opacity=\"0.8\">\n <rect class=\"fill-(--teal-100,#E0F4F1)\" width=\"38\" height=\"38\" rx=\"12\" />\n <path\n d=\"M11.2 24.8504H26.8C27.1575 24.8504 27.45 24.5579 27.45 24.2004V14.4504C27.45 14.0929 27.1575 13.8004 26.8 13.8004H20.7347C20.0319 13.8004 19.3453 13.5729 18.7847 13.1504L17.2247 11.9804C17.1109 11.8951 16.9769 11.8504 16.8347 11.8504H11.2C10.8425 11.8504 10.55 12.1429 10.55 12.5004V24.2004C10.55 24.5579 10.8425 24.8504 11.2 24.8504ZM26.8 26.8004H11.2C9.76591 26.8004 8.59998 25.6345 8.59998 24.2004V12.5004C8.59998 11.0663 9.76591 9.90039 11.2 9.90039H16.8347C17.3953 9.90039 17.9437 10.0832 18.3947 10.4204L19.9547 11.5904C20.1781 11.761 20.4544 11.8504 20.7347 11.8504H26.8C28.234 11.8504 29.4 13.0163 29.4 14.4504V24.2004C29.4 25.6345 28.234 26.8004 26.8 26.8004Z\"\n class=\"fill-(--teal-400,#4EBFB0)\" />\n </g>\n </svg>\n <h5 class=\"text-sm font-semibold text-(--font-primary-dark)\">{{ \"documentWorkspaces.detail.emptyTitle\" | transloco }}</h5>\n <p class=\"max-w-70 text-sm text-(--font-primary-base)\">{{ \"documentWorkspaces.detail.emptySubtitle\" | transloco }}</p>\n </div>\n } @else if (!filtered().length) {\n <p class=\"text-(--font-primary-muted) py-8 text-center text-sm\" data-testid=\"no-match\">{{ \"documentWorkspaces.detail.noMatch\" | transloco }}</p>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] content-start gap-4 pb-8\">\n @for (document of filtered(); track document.id) {\n <document-card\n [document]=\"document\"\n [workspaceRole]=\"workspace.currentUserRole\"\n [query]=\"query()\"\n [active]=\"document.id === previewedDocumentId()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(document)\"\n [selectable]=\"selection.canSelect(document)\"\n (opened)=\"togglePreview(document)\"\n (deleteRequested)=\"confirmDeleteDocuments([document])\"\n (selectedChange)=\"selection.toggle(document, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"${DOCUMENT_ROW_COLUMNS} py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <checkbox\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (checkedChange)=\"selection.toggleAll()\" />\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.detail.columnName\" | transloco }}</div>\n <div role=\"columnheader\" class=\"@max-3xl:hidden\">{{ \"documentWorkspaces.detail.columnSource\" | transloco }}</div>\n <div role=\"columnheader\" class=\"@max-3xl:hidden\">{{ \"documentWorkspaces.detail.columnSize\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.detail.columnDate\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (document of filtered(); track document.id) {\n <document-row\n [document]=\"document\"\n [workspaceRole]=\"workspace.currentUserRole\"\n [query]=\"query()\"\n [active]=\"document.id === previewedDocumentId()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(document)\"\n [selectable]=\"selection.canSelect(document)\"\n (opened)=\"togglePreview(document)\"\n (deleteRequested)=\"confirmDeleteDocuments([document])\"\n (selectedChange)=\"selection.toggle(document, $event)\" />\n }\n </div>\n }\n\n @if (hasMore()) {\n <div class=\"flex flex-col items-center gap-2\">\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">{{ \"documentWorkspaces.detail.loadMoreError\" | transloco: { error: message } }}</p>\n }\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.detail.loadMore\" | transloco }}\n </button>\n </div>\n }\n } @else {\n <p class=\"text-(--font-primary-muted) py-8 text-center text-sm\" data-testid=\"not-found\">{{ \"documentWorkspaces.errors.notFound\" | transloco }}</p>\n }\n }\n }\n </div>\n </div>\n\n <!-- The list gives up part of its width to the preview, and takes it back when the preview closes. -->\n <div\n #handle=\"resizeHandle\"\n class=\"group/handle flex w-3 shrink-0 cursor-col-resize items-center justify-center max-md:hidden\"\n [class.hidden]=\"!previewed()\"\n [resizeHandle]=\"previewWidth()\"\n (resizeHandleChange)=\"userPreviewWidth.set($event)\"\n [resizeMin]=\"280\"\n [resizeMax]=\"maxPreviewWidth()\"\n [attr.aria-label]=\"'documentWorkspaces.preview.resize' | transloco\">\n <span class=\"h-6 w-0.5 rounded-full bg-(--stroke-primary-light) opacity-0 group-hover/handle:opacity-100 group-focus-visible/handle:opacity-100 group-data-resizing/handle:opacity-100\"></span>\n </div>\n <aside\n class=\"sticky top-3 my-3 mr-3 flex h-[calc(100dvh-1.5rem)] max-h-[calc(100%-1.5rem)] shrink-0 self-start overflow-hidden rounded-(--radius-xl) bg-(--bg-neutral-white) transition-[width] duration-[350ms] ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none data-open:border data-open:border-(--stroke-primary-lighter) data-open:shadow-[0_4px_6px_-2px_rgba(45,57,58,0.05),0_10px_15px_-3px_rgba(45,57,58,0.1)] max-md:data-open:fixed max-md:data-open:inset-0 max-md:data-open:z-50 max-md:data-open:m-0 max-md:data-open:w-full! max-md:data-open:max-h-none max-md:data-open:rounded-none\"\n [class.transition-none]=\"handle.resizing()\"\n [style.width.px]=\"previewed() ? previewWidth() : 0\"\n [attr.data-open]=\"previewed() ? '' : null\"\n [attr.aria-hidden]=\"previewed() ? null : 'true'\"\n data-testid=\"preview\">\n @if (previewed(); as previewed) {\n <!-- Its own width, so the content does not reflow while the card opens. -->\n <document-preview-panel\n class=\"shrink-0 max-md:w-full!\"\n [style.width.px]=\"previewWidth()\"\n [document]=\"previewed\"\n [queryName]=\"queryName()\"\n (closed)=\"previewedDocumentId.set(null)\"\n (askFollowUp)=\"askFollowUp.emit($event)\" />\n }\n </aside>\n `\n})\nexport class WorkspaceDetail {\n readonly workspaceId = input.required<string>();\n /** The tus endpoint uploads go to — the host's `upload-endpoint`. */\n readonly uploadEndpoint = input(\"\");\n /** The document open in the preview, or `null`. Two-way: the host can open one, and is told when it is opened or closed. */\n readonly previewedDocumentId = model<string | null>(null);\n /** The query the preview web service renders a document for — the host's `query-name`. */\n readonly queryName = input(\"_query\");\n\n /** The user wants to leave for the list. */\n readonly back = output<void>();\n /** The workspace was deleted from here; the host has nothing left to show. */\n readonly deleted = output<Workspace>();\n /** The workspace was edited from here (title, description, tags). */\n readonly workspaceUpdated = output<Workspace>();\n /** \"Ask follow-up\" on a previewed document: the host's chat is what takes it from there. */\n readonly askFollowUp = output<AskFollowUp>();\n\n private readonly store = inject(WorkspacesStore);\n private readonly uploads = inject(UploadService);\n private readonly injector = inject(Injector);\n private readonly transloco = inject(TranslocoService);\n\n protected readonly viewMode = signal<ViewMode>(\"grid\");\n protected readonly query = signal(\"\");\n /** The search box, bound to `query`. */\n protected readonly searchForm = form(this.query);\n protected readonly status = signal<\"loading\" | \"ready\" | \"error\">(\"loading\");\n protected readonly loadError = signal<Message | null>(null);\n protected readonly refreshError = signal<string | null>(null);\n protected readonly refreshing = signal(false);\n protected readonly loadingMore = signal(false);\n protected readonly loadMoreError = signal<string | null>(null);\n /** The background refresh found the workspace gone from the server (deleted elsewhere). */\n protected readonly deletedElsewhere = signal(false);\n\n protected readonly workspace = computed(() => this.store.workspace(this.workspaceId()));\n // The list is the index: only a document the listing returns as indexed is shown. Transfers,\n // indexing and failures belong to the upload tracker, not to the workspace. A document with no\n // status at all is one the server does not track — it is simply there, so it stays listed.\n protected readonly documents = computed(() => this.store.documentsOf(this.workspaceId()).filter(d => !d.status));\n protected readonly previewed = computed(() => this.documents().find(d => d.id === this.previewedDocumentId()) ?? null);\n /** The width the user dragged the preview to, if they did. */\n protected readonly userPreviewWidth = signal<number | null>(null);\n /** The width of the view itself: the preview opens at 40% of it (280px at the least), as in the web element. */\n private readonly hostWidth = signal(0);\n protected readonly previewWidth = computed(() => this.userPreviewWidth() ?? Math.max(280, Math.round(this.hostWidth() * 0.4)));\n /** The list keeps at least 360px. */\n protected readonly maxPreviewWidth = computed(() => (this.hostWidth() ? this.hostWidth() - 360 : undefined));\n protected readonly hasMore = computed(() => this.store.hasMoreDocuments(this.workspaceId()));\n protected readonly filtered = computed(() => {\n const query = this.query().trim();\n return query ? this.documents().filter(d => matchesText(d.name, query)) : this.documents();\n });\n\n private readonly writable = computed(() => canWrite(this.workspace()?.currentUserRole));\n protected readonly selection = injectSelection({\n items: this.filtered,\n key: (document: WorkspaceDocument) => document.id,\n // Selecting only serves deleting, which a reader cannot do.\n selectable: () => this.writable()\n });\n\n // Background polling, re-scoped to whatever `workspaceId` is current on each\n // tick (no restart needed when the user switches workspaces): refreshes both\n // the workspace's own metadata (title, description, tags, owners/readers,\n // the current user's role) and its documents, so an edit or an access change\n // made elsewhere shows up here too, not just additions to the document list.\n // Comparing full snapshots (not just ids) catches an in-place edit that an\n // id-only diff would miss — a renamed workspace, a demoted role — and keeps\n // the backoff from widening on a tick that only looks unchanged by id.\n private readonly liveRefresh = injectLiveRefresh(\n async signal => {\n const id = this.workspaceId();\n const beforeWorkspace = JSON.stringify(this.store.workspace(id));\n const beforeDocuments = JSON.stringify(this.store.documentsOf(id));\n try {\n await Promise.all([this.store.fetch(id), this.store.loadDocuments(id, signal)]);\n } catch (error) {\n // Someone else deleted it: flag it and stop polling — there is nothing\n // left here to discover. The workspace stays in the shared cache (so its\n // name/description keep showing instead of the view going blank); `leave()`\n // drops it once the user actually goes back to the list.\n if (error instanceof ApiError && error.status === 404 && this.workspaceId() === id) {\n this.deletedElsewhere.set(true);\n this.selection.exit();\n this.liveRefresh.stop();\n return true;\n }\n throw error;\n }\n if (this.workspaceId() !== id) return false; // the user moved to another workspace meanwhile\n const documentsChanged = JSON.stringify(this.store.documentsOf(id)) !== beforeDocuments;\n const workspaceChanged = JSON.stringify(this.store.workspace(id)) !== beforeWorkspace;\n // Every tick, changed or not: an upload someone else just started shows no document yet, and the\n // tracker only learns of it from the status API. It stops by itself after a tick when nothing is moving.\n this.uploads.watch(id);\n return documentsChanged || workspaceChanged;\n },\n { intervalMs: 15_000, maxIntervalMs: 90_000 }\n );\n\n constructor() {\n effect(() => {\n this.workspaceId();\n untracked(() => void this.open());\n });\n\n // An access change made elsewhere can demote the user on the workspace they are looking at:\n // leave the selection mode before its entry point goes away, or its bar stays up with no way out.\n effect(() => {\n if (!this.writable()) untracked(() => this.selection.exit());\n });\n\n const host: HTMLElement = inject(ElementRef).nativeElement;\n if (typeof ResizeObserver !== \"undefined\") {\n const observer = new ResizeObserver(([entry]) => this.hostWidth.set(Math.round(entry.contentRect.width)));\n observer.observe(host);\n inject(DestroyRef).onDestroy(() => observer.disconnect());\n }\n\n inject(DestroyRef).onDestroy(() => this.uploads.watch(null));\n }\n\n private async open(): Promise<void> {\n this.selection.exit();\n this.previewedDocumentId.set(null);\n this.query.set(\"\");\n this.refreshError.set(null);\n this.loadMoreError.set(null);\n this.deletedElsewhere.set(false);\n await this.load();\n }\n\n /**\n * Leaving from here: once the background refresh found the workspace gone,\n * the cache still holds it (so its name kept showing) — drop it now, so a\n * stale card does not show it back on the list.\n */\n protected leave(): void {\n if (this.deletedElsewhere()) this.store.forget(this.workspaceId());\n this.back.emit();\n }\n\n /** Reads the workspace (when it is not cached) and its documents, for the workspace currently shown. */\n protected async load(): Promise<void> {\n const id = this.workspaceId();\n this.loadError.set(null);\n this.status.set(\"loading\");\n try {\n if (!this.store.workspace(id)) await this.store.fetch(id);\n await this.store.loadDocuments(id);\n } catch (error) {\n // The user may have moved to another workspace meanwhile: do not clobber what is shown now.\n if (this.workspaceId() !== id) return;\n this.loadError.set(workspaceErrorMessage(error));\n this.status.set(\"error\");\n return;\n }\n if (this.workspaceId() !== id) return;\n this.status.set(\"ready\");\n this.uploads.watch(id);\n this.liveRefresh.start();\n }\n\n /** Reads the documents again without leaving the view; a failure leaves the ones shown as they are. */\n protected async refresh(): Promise<void> {\n if (this.refreshing()) return;\n const id = this.workspaceId();\n this.refreshError.set(null);\n this.refreshing.set(true);\n try {\n await this.store.loadDocuments(id);\n if (this.workspaceId() === id) this.uploads.watch(id);\n } catch (error) {\n if (this.workspaceId() === id) this.refreshError.set(this.translate(workspaceErrorMessage(error)));\n } finally {\n this.refreshing.set(false);\n }\n }\n\n protected async loadMore(): Promise<void> {\n if (this.loadingMore()) return;\n const id = this.workspaceId();\n this.loadMoreError.set(null);\n this.loadingMore.set(true);\n try {\n await this.store.loadMoreDocuments(id);\n } catch (error) {\n if (this.workspaceId() === id) this.loadMoreError.set(this.translate(workspaceErrorMessage(error)));\n } finally {\n this.loadingMore.set(false);\n }\n }\n\n /** Opening the open one again closes it. */\n protected togglePreview(document: WorkspaceDocument): void {\n this.previewedDocumentId.update(id => (id === document.id ? null : document.id));\n }\n\n protected upload(workspace: Workspace): void {\n void UploadDocuments.call({ workspace, endpoint: this.uploadEndpoint() }, { injector: this.injector });\n }\n\n protected share(workspace: Workspace): void {\n void ShareWorkspace.call({ workspaceId: workspace.id }, { injector: this.injector });\n }\n\n protected async edit(workspace: Workspace): Promise<void> {\n const result = await EditWorkspace.call({ mode: \"edit\", workspace }, { injector: this.injector });\n if (typeof result === \"object\") this.workspaceUpdated.emit((result as WorkspaceEditResult).workspace);\n }\n\n protected async confirmDeleteWorkspace(workspace: Workspace): Promise<void> {\n const config: ConfirmDialogConfig = {\n title: this.transloco.translate(\"documentWorkspaces.list.deleteTitle\"),\n description: this.transloco.translate(\"documentWorkspaces.common.deleteWarning\", { name: workspace.name }),\n emphasis: workspace.name,\n destructive: true,\n action: async () => {\n const { failed } = await this.store.remove([workspace.id]);\n if (failed.length) throw new Error(failed[0].error);\n }\n };\n const result = await ConfirmAction.call(config, { injector: this.injector });\n if (result === \"dialog-confirm\") this.deleted.emit(workspace);\n }\n\n protected deleteSelected(): void {\n void this.confirmDeleteDocuments(this.selection.selectedItems());\n }\n\n protected async confirmDeleteDocuments(targets: WorkspaceDocument[]): Promise<void> {\n if (!targets.length) return;\n const id = this.workspaceId();\n const ids = targets.map(d => d.id);\n const single = targets.length === 1;\n const subject = single ? targets[0].name : this.transloco.translate(\"documentWorkspaces.detail.documentCount\", { count: targets.length });\n\n const config: ConfirmDialogConfig = {\n title: single\n ? this.transloco.translate(\"documentWorkspaces.detail.deleteDocTitle\")\n : this.transloco.translate(\"documentWorkspaces.detail.deleteDocumentsTitle\", { count: targets.length }),\n description: this.transloco.translate(\"documentWorkspaces.common.deleteWarning\", { name: subject }),\n emphasis: subject,\n destructive: true,\n action: () => this.store.removeDocuments(id, ids)\n };\n const result = await ConfirmAction.call(config, { injector: this.injector });\n if (result === \"dialog-confirm\") this.selection.exit();\n }\n\n private translate(message: Message): string {\n return \"key\" in message ? this.transloco.translate(message.key, message.params) : message.text;\n }\n}\n","import { assertInInjectionContext, computed, effect, inject, InjectionToken, Injector, signal, untracked } from \"@angular/core\";\nimport { EditWorkspace, WorkspaceEditResult } from \"../components/detail/workspace-edit.dialog\";\nimport type { Workspace, WorkspaceColor, WorkspaceRole } from \"../models/workspace.model\";\nimport { WorkspacesStore } from \"../store/workspaces.store\";\nimport { injectWorkspaceFilter, WorkspaceSearchFields, WorkspaceTab } from \"./inject-workspace-filter\";\n\n/** What a host chat reads to build its own mention menu. */\nexport interface MentionSelection {\n ids: string[];\n count: number;\n}\n\n/** One displayed workspace, with whether it is ticked. */\nexport interface MentionItem {\n id: string;\n name: string;\n description: string;\n color: WorkspaceColor;\n documentCount: number;\n /** The current user's role on it: a host can mark the read-only rows (`reader`) without hiding them. */\n currentUserRole: WorkspaceRole;\n selected: boolean;\n}\n\n/** The categories, in order: the translation key of each one's label. */\nexport const MENTION_TABS: readonly { id: WorkspaceTab; labelKey: string }[] = [\n { id: \"all\", labelKey: \"documentWorkspaces.mention.tabAll\" },\n { id: \"editor\", labelKey: \"documentWorkspaces.mention.tabOwned\" },\n { id: \"reader\", labelKey: \"documentWorkspaces.mention.tabShared\" }\n];\n\nexport interface WorkspaceMentionOptions {\n /** What the search matches. Read reactively; the name only when absent. */\n searchFields?: () => WorkspaceSearchFields;\n /**\n * The ticked ids, when the host owns the selection (controlled mode): the ticks are then\n * the host's and the store's are left alone. `undefined` — the default — keeps them in the store.\n */\n selectedIds?: () => readonly string[] | undefined;\n /** An extra condition a workspace must meet to be offered, on top of the tab and the search. */\n predicate?: () => ((workspace: Workspace) => boolean) | undefined;\n /** The ticks changed because of this menu. */\n onSelectionChange?: (selection: MentionSelection) => void;\n /** A workspace was created from here, and ticked. */\n onCreated?: (workspace: Workspace) => void;\n}\n\n/**\n * The state behind a \"mention a workspace\" menu — search, category, ticks, \"select all\"\n * and \"new workspace\" — with no markup, so any layout can be drawn over it.\n *\n * The ticks are the store's unless the host passes `selectedIds`: however many menus\n * are on screen they then show the same thing. \"Select all\" works on what is displayed,\n * so on the current category and search. Every workspace is offered, the ones shared\n * read-only too — mentioning one only reads it.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectWorkspaceMention(options: WorkspaceMentionOptions = {}) {\n assertInInjectionContext(injectWorkspaceMention);\n\n const store = inject(WorkspacesStore);\n const injector = inject(Injector);\n const filter = injectWorkspaceFilter(store.workspaces, { fields: options.searchFields });\n\n const loadStatus = signal<\"loading\" | \"ready\" | \"error\">(store.loaded() ? \"ready\" : \"loading\");\n if (!store.loaded()) {\n // Another list or picker may be the one that loads: the store is shared, and this follows it.\n store.ensureLoaded().then(\n () => loadStatus.set(\"ready\"),\n () => loadStatus.set(\"error\")\n );\n }\n\n const loadingMore = signal(false);\n\n /** The ids the host passed in controlled mode — possibly of workspaces deleted since. */\n const hostIds = computed(() => options.selectedIds?.());\n // The store drops a deleted workspace from its own ticks, but a host's ticks are its own: in\n // controlled mode the ids are cut down to the workspaces that exist. Only once the list is\n // complete — a workspace past the loaded pages is not a deleted one.\n const listComplete = computed(() => store.loaded() && !store.hasMore() && !store.truncated());\n const selectedIds = computed<readonly string[]>(() => {\n const host = hostIds();\n if (!host) return store.selectedIds();\n if (!listComplete()) return host;\n const known = new Set(store.workspaces().map(w => w.id));\n return host.filter(id => known.has(id));\n });\n\n effect(() => {\n const host = hostIds();\n const kept = selectedIds();\n if (!host || host.length === kept.length) return;\n // The host hears about it like any other change; `untracked` keeps its callback's reads out of this effect.\n untracked(() => options.onSelectionChange?.({ ids: [...kept], count: kept.length }));\n });\n const selectedSet = computed(() => new Set(selectedIds()));\n\n const items = computed<MentionItem[]>(() => {\n const only = options.predicate?.();\n return filter\n .filtered()\n .filter(w => !only || only(w))\n .map(w => ({\n id: w.id,\n name: w.name,\n description: w.description,\n color: w.color,\n documentCount: w.documentCount,\n currentUserRole: w.currentUserRole,\n selected: selectedSet().has(w.id)\n }));\n });\n\n const count = computed(() => selectedIds().length);\n const ticked = computed(() => items().filter(item => item.selected).length);\n // `length > 0` matters: [].every() is true, which would label an empty list \"Unselect all\".\n const allSelected = computed(() => items().length > 0 && ticked() === items().length);\n const partiallySelected = computed(() => ticked() > 0 && !allSelected());\n\n /** Writes the ticks where they live, and says so. */\n const commit = (ids: string[]) => {\n if (!options.selectedIds?.()) {\n const next = new Set(ids);\n for (const id of selectedSet()) if (!next.has(id)) store.toggleSelected(id, false);\n for (const id of next) if (!selectedSet().has(id)) store.toggleSelected(id, true);\n }\n options.onSelectionChange?.({ ids: [...ids], count: ids.length });\n };\n\n const toggle = (id: string, checked: boolean) => {\n const without = selectedIds().filter(selected => selected !== id);\n commit(checked ? [...without, id] : without);\n };\n\n return {\n /** The search text; writable, so a text box can bind to it. */\n query: filter.query,\n clearQuery: filter.clearQuery,\n /** The category: all, the ones you own (`editor`), the ones shared with you (`reader`). */\n tab: filter.tab,\n tabs: MENTION_TABS,\n /** The workspaces in each category — the whole list, not what the search left. */\n counts: filter.counts,\n /** Whether the first page is loading, ready or failed. */\n status: loadStatus.asReadonly(),\n /** More workspaces than the loaded ones exist on the server: `loadMore()` fetches the next page. */\n hasMore: store.hasMore,\n /** The loading cap is reached: some workspaces will never be offered, search included. */\n truncated: store.truncated,\n /** A page is being fetched by `loadMore()`. */\n loadingMore: loadingMore.asReadonly(),\n /** Loads the next page; the search and the categories then run over it too. */\n async loadMore(): Promise<void> {\n loadingMore.set(true);\n try {\n await store.loadMore();\n } finally {\n loadingMore.set(false);\n }\n },\n /** What is offered: the category, the search and the predicate applied. */\n items,\n /** How many are ticked, displayed or not. */\n count,\n /** The ticked ids, displayed or not. */\n ids: selectedIds,\n allSelected,\n partiallySelected,\n isSelected: (id: string) => selectedSet().has(id),\n toggle,\n /** Ticks every displayed workspace, or unticks them all when they already are. */\n toggleAll: () => {\n const displayed = items().map(item => item.id);\n if (allSelected()) {\n const drop = new Set(displayed);\n commit(selectedIds().filter(id => !drop.has(id)));\n } else {\n commit([...new Set([...selectedIds(), ...displayed])]);\n }\n },\n /** Ticks a workspace from outside — a host that has its own way of choosing one. */\n select: (id: string) => toggle(id, true),\n /**\n * Opens the \"new workspace\" dialog; the workspace it makes is ticked and shown (the category\n * and the search are reset so it is not hidden). Resolves with it, or `null` when dismissed.\n *\n * The dialog is deliberately not tied to the caller's `DestroyRef`: a menu that closes on\n * focus-out (a hover-mounted panel) would end it mid-save.\n */\n async create(): Promise<Workspace | null> {\n const result = await EditWorkspace.call({ mode: \"add\" }, { injector });\n if (typeof result !== \"object\") return null;\n const { workspace } = result as WorkspaceEditResult;\n filter.tab.set(\"all\");\n filter.clearQuery();\n toggle(workspace.id, true);\n options.onCreated?.(workspace);\n return workspace;\n }\n };\n}\n\nexport type WorkspaceMentionApi = ReturnType<typeof injectWorkspaceMention>;\n\n/** Provided by `<workspace-mention>` to the pieces placed inside it. */\nexport const WORKSPACE_MENTION = new InjectionToken<WorkspaceMentionApi>(\"WORKSPACE_MENTION\");\n\n/** The enclosing `<workspace-mention>`, for a piece that cannot work outside one. */\nexport function injectWorkspaceMentionScope(): WorkspaceMentionApi {\n const scope = inject(WORKSPACE_MENTION, { optional: true });\n if (!scope) throw new Error(\"[atomic] This piece must be placed inside a <workspace-mention>.\");\n return scope;\n}\n","/**\n * Whether the host put something of its own in a slot: an element, or text that is not blank.\n * Comments and whitespace — what an empty `@if` or a line break leaves behind — do not count.\n */\nexport function hasContent(slot: HTMLElement): boolean {\n return Array.from(slot.childNodes).some(node => node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && !!node.textContent?.trim()));\n}\n","import { AfterContentInit, ChangeDetectionStrategy, Component, ElementRef, input, signal, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, type ButtonVariants, PlusIcon } from \"@sinequa/galactik\";\nimport { hasContent } from \"./has-content\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\n\n/**\n * The \"+ New workspace\" button of the enclosing `<workspace-mention>`: it opens the same\n * dialog as the workspaces page, and the workspace it makes is ticked.\n *\n * Its look is the host's: `variant`, `scheme` and `size` are the button's own, and content\n * placed inside replaces the icon and the label.\n *\n * ```html\n * <workspace-mention-new variant=\"primary\" size=\"md\">Add a workspace</workspace-mention-new>\n * ```\n */\n@Component({\n selector: \"workspace-mention-new\",\n imports: [TranslocoPipe, ButtonComponent, PlusIcon],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"inline-block\" },\n template: `\n <button type=\"button\" [variant]=\"variant()\" [scheme]=\"scheme()\" [size]=\"size()\" (click)=\"create()\">\n <span #slot class=\"contents\"><ng-content /></span>\n @if (!custom()) {\n <plus-icon />\n {{ \"documentWorkspaces.mention.newWorkspace\" | transloco }}\n }\n </button>\n `\n})\nexport class WorkspaceMentionNew implements AfterContentInit {\n /** The button's variant. */\n readonly variant = input<ButtonVariants[\"variant\"]>(\"tertiary\");\n /** The button's colour scheme. */\n readonly scheme = input<ButtonVariants[\"scheme\"]>(\"neutral\");\n /** The button's size. */\n readonly size = input<ButtonVariants[\"size\"]>(\"sm\");\n\n private readonly mention = injectWorkspaceMentionScope();\n private readonly slot = viewChild.required<ElementRef<HTMLElement>>(\"slot\");\n /** Whether the host put its own content inside: the icon and the label then stay out. */\n protected readonly custom = signal(false);\n\n ngAfterContentInit(): void {\n this.custom.set(hasContent(this.slot().nativeElement));\n }\n\n protected create(): void {\n void this.mention.create();\n }\n}\n","import { ChangeDetectionStrategy, Component } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { CheckboxComponent } from \"@sinequa/galactik\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\nimport { WorkspaceIcon } from \"../shared/workspace-icon\";\n\n/**\n * The workspaces of the enclosing `<workspace-mention>` as rows to tick: the default\n * look of the menu. A host that wants another look loops over the scope's `items()` itself.\n *\n * It fills the height its host gives it and scrolls inside it; with no fixed height around it,\n * it stops at 18rem — or at `--workspace-mention-rows-max-height`, which the host can set (`none` lifts it).\n */\n@Component({\n selector: \"workspace-mention-rows\",\n imports: [TranslocoPipe, CheckboxComponent, WorkspaceIcon],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block min-h-0 max-h-(--workspace-mention-rows-max-height,18rem) flex-1 overflow-auto\" },\n template: `\n @switch (mention.status()) {\n @case (\"loading\") {\n <p class=\"px-(--spacing-xs) py-(--spacing-2xs) text-(length:--size-base) text-(--font-primary-muted)\">{{ \"documentWorkspaces.mention.loading\" | transloco }}</p>\n }\n @case (\"error\") {\n <p class=\"px-(--spacing-xs) py-(--spacing-2xs) text-(length:--size-base) text-(--font-error-base)\" role=\"alert\">{{ \"documentWorkspaces.mention.loadError\" | transloco }}</p>\n }\n @default {\n @if (!mention.items().length) {\n @if (mention.query()) {\n <p class=\"px-(--spacing-xs) py-(--spacing-2xs) text-(length:--size-base) text-(--font-primary-muted)\" data-testid=\"no-match\">{{ \"documentWorkspaces.mention.noMatch\" | transloco }}</p>\n } @else {\n <p class=\"px-(--spacing-xs) py-(--spacing-2xs) text-(length:--size-base) text-(--font-primary-muted)\" data-testid=\"empty\">{{ \"documentWorkspaces.mention.empty\" | transloco }}</p>\n }\n } @else {\n <!-- The checkbox carries the semantics (a checkbox in a list): no listbox/option around it. -->\n <ul class=\"flex flex-col\">\n @for (item of mention.items(); track item.id) {\n <li class=\"rounded-(--radius-sm) hover:bg-(--bg-primary-lightest) data-selected:bg-(--bg-primary-lighter)\" [attr.data-selected]=\"item.selected ? '' : null\">\n <checkbox class=\"w-full items-center gap-(--spacing-xs) px-(--spacing-xs) py-(--spacing-2xs)\" [checked]=\"item.selected\" (checkedChange)=\"mention.toggle(item.id, $event)\">\n <span class=\"flex items-center gap-(--spacing-xs)\">\n <workspace-icon size=\"md\" [color]=\"item.color\" />\n <span class=\"flex min-w-0 flex-col\">\n <span class=\"truncate text-(length:--size-base) font-semibold text-(--font-neutral-black)\">{{ item.name }}</span>\n <span class=\"text-(length:--size-sm) text-(--font-primary-muted)\">{{ \"documentWorkspaces.mention.documentCount\" | transloco: { count: item.documentCount } }}</span>\n </span>\n </span>\n </checkbox>\n </li>\n }\n </ul>\n }\n }\n }\n `\n})\nexport class WorkspaceMentionRows {\n protected readonly mention = injectWorkspaceMentionScope();\n}\n","import { ChangeDetectionStrategy, Component, input } from \"@angular/core\";\nimport { form, FormField } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { type InputWrapVariants, SearchInputComponent } from \"@sinequa/galactik\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\n\n/** The search box of the enclosing `<workspace-mention>`: it narrows what the menu offers. */\n@Component({\n selector: \"workspace-mention-search\",\n imports: [TranslocoPipe, FormField, SearchInputComponent],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block\" },\n template: `\n <SearchInput\n class=\"w-full\"\n [size]=\"size()\"\n [textSize]=\"textSize()\"\n [formField]=\"searchForm\"\n [placeholder]=\"placeholder() ?? ('documentWorkspaces.mention.searchPlaceholder' | transloco)\"\n [aria-label]=\"ariaLabel() ?? placeholder() ?? ('documentWorkspaces.mention.searchPlaceholder' | transloco)\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n `\n})\nexport class WorkspaceMentionSearch {\n /** The size of the box, as `<SearchInput>` sizes it. */\n readonly size = input<InputWrapVariants[\"size\"]>(\"md\");\n /** The size of the text, as `<SearchInput>` sizes it; follows `size` when absent. */\n readonly textSize = input<InputWrapVariants[\"textSize\"]>();\n /** The hint shown in the empty box; the translated \"Search workspace...\" when absent. */\n readonly placeholder = input<string>();\n /** The accessible name of the box; the placeholder when absent. */\n readonly ariaLabel = input<string | undefined>(undefined, { alias: \"aria-label\" });\n\n private readonly mention = injectWorkspaceMentionScope();\n\n /** Bound to the scope's own query signal. */\n protected readonly searchForm = form(this.mention.query);\n}\n","import { ChangeDetectionStrategy, Component } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { CheckboxComponent } from \"@sinequa/galactik\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\n\n/**\n * The \"select all\" checkbox of the enclosing `<workspace-mention>`. It acts on what is\n * displayed — the current category and search — and shows a mixed state when only some are ticked.\n * Absent while the list loads and while nothing is displayed: there is nothing to select.\n */\n@Component({\n selector: \"workspace-mention-select-all\",\n imports: [TranslocoPipe, CheckboxComponent],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block\" },\n template: `\n @if (mention.status() === \"ready\" && mention.items().length) {\n <checkbox class=\"items-center gap-(--spacing-xs)\" [checked]=\"mention.allSelected()\" [indeterminate]=\"mention.partiallySelected()\" (checkedChange)=\"mention.toggleAll()\">\n <span class=\"text-(length:--size-base) text-(--font-neutral-black)\">{{ (mention.allSelected() ? \"documentWorkspaces.mention.unselectAll\" : \"documentWorkspaces.mention.selectAll\") | transloco }}</span>\n </checkbox>\n }\n `\n})\nexport class WorkspaceMentionSelectAll {\n protected readonly mention = injectWorkspaceMentionScope();\n}\n","import {\n AfterContentInit,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n forwardRef,\n input,\n output,\n signal,\n viewChild\n} from \"@angular/core\";\nimport type { WorkspaceSearchFields } from \"../../composables/inject-workspace-filter\";\nimport { injectWorkspaceMention, MentionSelection, WORKSPACE_MENTION, WorkspaceMentionApi } from \"../../composables/inject-workspace-mention\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { hasContent } from \"./has-content\";\nimport { WorkspaceMentionNew } from \"./workspace-mention-new\";\nimport { WorkspaceMentionRows } from \"./workspace-mention-rows\";\nimport { WorkspaceMentionSearch } from \"./workspace-mention-search\";\nimport { WorkspaceMentionSelectAll } from \"./workspace-mention-select-all\";\n\n/**\n * Mention workspaces: the state of the menu (search, category, ticks, \"select all\", \"new\n * workspace\") shared with the pieces placed inside it.\n *\n * Empty, it is the ready-made menu — the rows, plus the search box, \"select all\" and \"new\n * workspace\" you switch on:\n *\n * ```html\n * <workspace-mention searchable selectAll allowCreate (selectionChanged)=\"onMention($event)\" />\n * ```\n *\n * With content, it only provides the state, and you lay it out with the pieces\n * (`workspace-mention-search`, `-select-all`, `-new`, `-tabs`) and your own markup over `items()`:\n *\n * ```html\n * <workspace-mention #ws=\"workspaceMention\" searchFields=\"name+description\" [selectedIds]=\"ids()\" (selectionChanged)=\"ids.set($event.ids)\">\n * <workspace-mention-search />\n * @for (w of ws.items(); track w.id) { <my-card [workspace]=\"w\" (click)=\"ws.toggle(w.id, !w.selected)\" /> }\n * </workspace-mention>\n * ```\n *\n * The ticks are the store's, so every menu on screen shows the same ones — unless `selectedIds`\n * is passed: the ticks are then the host's, and it hears each change from `selectionChanged`.\n */\n@Component({\n selector: \"workspace-mention\",\n exportAs: \"workspaceMention\",\n imports: [WorkspaceMentionNew, WorkspaceMentionRows, WorkspaceMentionSearch, WorkspaceMentionSelectAll],\n providers: [{ provide: WORKSPACE_MENTION, useExisting: forwardRef(() => WorkspaceMention) }],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"contents\" },\n template: `\n <div #slot class=\"contents\"><ng-content /></div>\n @if (!custom()) {\n <div class=\"flex min-h-0 flex-1 flex-col gap-(--spacing-2xs)\">\n @if (searchable() || allowCreate()) {\n <div class=\"flex items-center gap-(--spacing-2xs) px-(--spacing-xs) pt-(--spacing-2xs)\">\n @if (searchable()) {\n <workspace-mention-search class=\"min-w-0 flex-1\" />\n }\n @if (allowCreate()) {\n <workspace-mention-new class=\"shrink-0\" />\n }\n </div>\n }\n @if (selectAll()) {\n <workspace-mention-select-all class=\"px-(--spacing-xs)\" />\n }\n <workspace-mention-rows />\n </div>\n }\n `\n})\nexport class WorkspaceMention implements WorkspaceMentionApi, AfterContentInit {\n /** What the search matches: the name only (default), or the name and the description. */\n readonly searchFields = input<WorkspaceSearchFields>(\"name\");\n /** The ticked ids when the host owns the selection; absent, they live in the store. */\n readonly selectedIds = input<readonly string[] | undefined>(undefined);\n /** An extra condition a workspace must meet to be offered, on top of the category and the search. */\n readonly predicate = input<((workspace: Workspace) => boolean) | undefined>(undefined);\n /** The ready-made menu only: shows the search box. */\n readonly searchable = input(false, { transform: booleanAttribute });\n /** The ready-made menu only: shows \"select all\". */\n readonly selectAll = input(false, { transform: booleanAttribute });\n /** The ready-made menu only: shows \"new workspace\". */\n readonly allowCreate = input(false, { transform: booleanAttribute });\n\n /** The ticks changed because of this menu. */\n readonly selectionChanged = output<MentionSelection>();\n /** A workspace was created from here, and ticked. */\n readonly workspaceCreated = output<Workspace>();\n\n private readonly slot = viewChild.required<ElementRef<HTMLElement>>(\"slot\");\n /** Whether the host put its own content inside: the ready-made menu then stays out. */\n protected readonly custom = signal(false);\n\n private readonly state = injectWorkspaceMention({\n searchFields: () => this.searchFields(),\n selectedIds: () => this.selectedIds(),\n predicate: () => this.predicate(),\n onSelectionChange: selection => this.selectionChanged.emit(selection),\n onCreated: workspace => this.workspaceCreated.emit(workspace)\n });\n\n // The state, as the pieces and a template reference (`#ws=\"workspaceMention\"`) read it.\n readonly query = this.state.query;\n readonly clearQuery = this.state.clearQuery;\n readonly tab = this.state.tab;\n readonly tabs = this.state.tabs;\n readonly counts = this.state.counts;\n readonly status = this.state.status;\n readonly hasMore = this.state.hasMore;\n readonly truncated = this.state.truncated;\n readonly loadingMore = this.state.loadingMore;\n readonly loadMore = this.state.loadMore;\n readonly items = this.state.items;\n readonly count = this.state.count;\n readonly ids = this.state.ids;\n readonly allSelected = this.state.allSelected;\n readonly partiallySelected = this.state.partiallySelected;\n readonly isSelected = this.state.isSelected;\n readonly toggle = this.state.toggle;\n readonly toggleAll = this.state.toggleAll;\n readonly select = this.state.select;\n readonly create = this.state.create;\n\n ngAfterContentInit(): void {\n // The projected nodes are in place before the view is first drawn: no flash of the default.\n this.custom.set(hasContent(this.slot().nativeElement));\n }\n}\n","import { booleanAttribute, ChangeDetectionStrategy, Component, computed, input, output, viewChild } from \"@angular/core\";\nimport type { WorkspaceSearchFields } from \"../../composables/inject-workspace-filter\";\nimport type { MentionSelection } from \"../../composables/inject-workspace-mention\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { WorkspaceMention } from \"./workspace-mention\";\n\nexport type { MentionSelection } from \"../../composables/inject-workspace-mention\";\n\n/**\n * The workspaces as rows to tick, for the \"+\" / \"@\" menu of a host chat — the ready-made\n * `<workspace-mention>`, with the search box, \"select all\" and \"new workspace\" switched on\n * by `searchable`, `selectAll` and `allowCreate` (all off by default).\n *\n * The ticks are the store's, not this component's, unless `selectedIds` is passed: however\n * many of these are on screen (one per open menu), and wherever else the selection is read,\n * they then show the same thing. Every workspace is listed, the ones shared read-only too —\n * mentioning one only reads it.\n *\n * To lay the pieces out yourself, use `<workspace-mention>` with its own content instead.\n */\n@Component({\n selector: \"workspace-mention-list\",\n imports: [WorkspaceMention],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block\" },\n template: `\n <workspace-mention\n [searchable]=\"searchable()\"\n [selectAll]=\"selectAll()\"\n [allowCreate]=\"allowCreate()\"\n [searchFields]=\"searchFields()\"\n [selectedIds]=\"selectedIds()\"\n (selectionChanged)=\"selectionChanged.emit($event)\"\n (workspaceCreated)=\"workspaceCreated.emit($event)\" />\n `\n})\nexport class WorkspaceMentionList {\n /** Shows a search box. */\n readonly searchable = input(false, { transform: booleanAttribute });\n /** Shows \"select all\". */\n readonly selectAll = input(false, { transform: booleanAttribute });\n /** Shows \"new workspace\". */\n readonly allowCreate = input(false, { transform: booleanAttribute });\n /** What the search matches: the name only (default), or the name and the description. */\n readonly searchFields = input<WorkspaceSearchFields>(\"name\");\n /** The ticked ids when the host owns the selection; absent, they live in the store. */\n readonly selectedIds = input<readonly string[] | undefined>(undefined);\n\n /** The ticks changed because of this list. */\n readonly selectionChanged = output<MentionSelection>();\n /** A workspace was created from here, and ticked. */\n readonly workspaceCreated = output<Workspace>();\n\n private readonly mention = viewChild.required(WorkspaceMention);\n\n /** The rows, for this list and for a host that draws its own. */\n readonly items = computed(() => this.mention().items());\n\n /** Ticks a workspace from outside — a host that has its own way of choosing one. */\n select(id: string): void {\n this.mention().select(id);\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { BadgeComponent, TabComponent, TabListComponent, TabsComponent } from \"@sinequa/galactik\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\nimport type { WorkspaceTab } from \"../../composables/inject-workspace-filter\";\n\n/**\n * The categories of the enclosing `<workspace-mention>` — all, owned, shared — each with the\n * number of workspaces it holds. A host that lays the categories out differently (a sidebar)\n * reads `tabs`, `tab` and `counts` from the scope instead.\n */\n@Component({\n selector: \"workspace-mention-tabs\",\n imports: [TranslocoPipe, BadgeComponent, TabComponent, TabListComponent, TabsComponent],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block max-w-full\" },\n template: `\n <Tabs class=\"max-w-full\">\n <TabList\n variant=\"inner\"\n class=\"w-fit max-w-full\"\n selectionMode=\"explicit\"\n [softDisabled]=\"false\"\n [attr.aria-label]=\"ariaLabel() ?? ('documentWorkspaces.mention.tabsLabel' | transloco)\"\n [selectedTab]=\"mention.tab()\"\n (selectedTabChange)=\"onTabChange($event)\">\n @for (item of mention.tabs; track item.id) {\n <Tab [value]=\"item.id\" variant=\"inner\">\n <span>{{ item.labelKey | transloco }}</span>\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"shrink-0\" [attr.data-testid]=\"'badge-' + item.id\">{{ mention.counts()[item.id] }}</Badge>\n </Tab>\n }\n </TabList>\n </Tabs>\n `\n})\nexport class WorkspaceMentionTabs {\n /** The accessible name of the category list; the translated \"Categories\" when absent. */\n readonly ariaLabel = input<string | undefined>(undefined, { alias: \"aria-label\" });\n\n protected readonly mention = injectWorkspaceMentionScope();\n\n protected onTabChange(tab: string | undefined): void {\n if (tab) this.mention.tab.set(tab as WorkspaceTab);\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input, output, signal } from \"@angular/core\";\nimport { form, FormField, maxLength } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckIcon, InputComponent, InputControlDirective, PlusIcon, XMarkIcon } from \"@sinequa/galactik\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { workspaceErrorMessage } from \"../../utils/error-message\";\nimport type { Message } from \"../../utils/message\";\nimport { canWrite } from \"../../utils/workspace-role\";\nimport { WorkspaceIcon } from \"../shared/workspace-icon\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\n\n/** What \"save\" attaches to a workspace: a document that already exists, by name. */\nexport interface PickerDocument {\n name: string;\n}\n\nexport const NEW_WORKSPACE_NAME_MAX = 50;\n\n/** How long the check mark stays before the picker says it is done. */\nconst SAVE_FEEDBACK_MS = 900;\nconst MOVE_FEEDBACK_MS = 700;\n\n/**\n * Picks the workspace to put documents in — \"Save to workspace\", or \"Move to\n * workspace\" when they come from another one — and can make a new one on the spot.\n *\n * Only the workspaces the user can write to are offered: attaching a document is a\n * write, so a workspace shared read-only stays out (it stays visible wherever it is\n * merely read). When moving, the workspace the documents come from is left out too.\n *\n * It does the work (the store's `saveDocuments` / `moveDocuments`) and says when it is\n * over — `completed` — once the check mark has been seen; whoever shows it (a popover,\n * a bottom sheet) decides what closing means.\n */\n@Component({\n selector: \"workspace-picker\",\n imports: [TranslocoPipe, FormField, ButtonComponent, CheckIcon, InputComponent, InputControlDirective, PlusIcon, WorkspaceIcon, WorkspaceMessage, XMarkIcon],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"flex flex-col gap-2\" },\n template: `\n @if (doneWorkspace(); as done) {\n <p class=\"text-sm text-(--font-primary-base)\" role=\"status\" data-testid=\"done\">\n {{ \"documentWorkspaces.picker.addedTo\" | transloco: { name: done.name } }}\n </p>\n }\n\n @if (creating()) {\n <div class=\"flex flex-col gap-1.5\" data-testid=\"new-form\">\n <label for=\"new-workspace-name\" class=\"text-xs font-semibold text-(--font-primary-base)\">{{ \"documentWorkspaces.picker.workspaceName\" | transloco }}</label>\n <div class=\"flex items-center gap-1\">\n <input-group class=\"min-w-0 flex-1\" size=\"md\" [scheme]=\"duplicate() ? 'error' : 'default'\">\n <input\n input-control\n id=\"new-workspace-name\"\n type=\"text\"\n autocomplete=\"off\"\n [formField]=\"newForm.name\"\n [attr.placeholder]=\"'documentWorkspaces.picker.namePlaceholder' | transloco\"\n [attr.aria-invalid]=\"duplicate() ? 'true' : null\"\n (keydown)=\"onNameKeydown($event)\" />\n </input-group>\n <button\n variant=\"secondary\"\n size=\"md\"\n [iconOnly]=\"true\"\n [disabled]=\"submitting()\"\n [attr.aria-label]=\"'documentWorkspaces.common.cancel' | transloco\"\n (click)=\"cancelNew()\">\n <x-mark-icon />\n </button>\n <button size=\"md\" [iconOnly]=\"true\" [disabled]=\"!canCreate() || submitting()\" [attr.aria-label]=\"'documentWorkspaces.common.create' | transloco\" (click)=\"create()\">\n <check-icon />\n </button>\n </div>\n @if (duplicate()) {\n <p role=\"alert\" class=\"text-xs text-(--font-error-base)\">{{ \"documentWorkspaces.picker.duplicateName\" | transloco }}</p>\n }\n </div>\n } @else {\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-xs font-semibold text-(--font-primary-muted)\">{{ \"documentWorkspaces.list.title\" | transloco }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"creating.set(true)\">{{ \"documentWorkspaces.picker.newWorkspace\" | transloco }}</button>\n </div>\n }\n\n <ul class=\"flex max-h-64 flex-col gap-1 overflow-auto\" [class.opacity-50]=\"creating()\" [attr.inert]=\"creating() ? '' : null\" data-testid=\"list\">\n @switch (status()) {\n @case (\"loading\") {\n <li class=\"px-1 py-2 text-sm text-(--font-primary-muted)\">{{ \"documentWorkspaces.mention.loading\" | transloco }}</li>\n }\n @case (\"error\") {\n <li class=\"flex items-center gap-2 px-1 py-2 text-sm text-(--font-error-base)\" data-testid=\"load-error\">\n {{ \"documentWorkspaces.mention.loadError\" | transloco }}\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </li>\n }\n @default {\n @for (workspace of rows(); track workspace.id) {\n <li class=\"flex items-center gap-2 rounded-lg px-1 py-1 data-new:bg-(--bg-primary-lightest)\" [attr.data-new]=\"isNew(workspace) ? '' : null\" [attr.data-id]=\"workspace.id\">\n <workspace-icon size=\"md\" [color]=\"workspace.color\" />\n <span class=\"min-w-0 flex-1 truncate text-sm text-(--font-neutral-black)\">{{ workspace.name }}</span>\n @if (doneId() === workspace.id) {\n <button size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.picker.addedTo' | transloco: { name: workspace.name }\" disabled>\n <check-icon />\n </button>\n } @else {\n <button\n variant=\"secondary\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"busyId() !== null || doneId() !== null\"\n [attr.aria-label]=\"'documentWorkspaces.picker.addTo' | transloco: { name: workspace.name }\"\n (click)=\"add(workspace)\">\n <plus-icon />\n </button>\n }\n </li>\n } @empty {\n <li class=\"px-1 py-2 text-sm text-(--font-primary-muted)\" data-testid=\"empty\">{{ \"documentWorkspaces.picker.noWorkspaces\" | transloco }}</li>\n }\n }\n }\n </ul>\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"text-xs text-(--font-error-base)\"><workspace-message [message]=\"message\" /></p>\n }\n `\n})\nexport class WorkspacePicker {\n /** `save` attaches `documents` to the chosen workspace; `move` takes `documentIds` from `fromWorkspaceId` to it. */\n readonly mode = input<\"save\" | \"move\">(\"save\");\n readonly documents = input<readonly PickerDocument[]>([]);\n readonly fromWorkspaceId = input<string | null>(null);\n readonly documentIds = input<readonly string[]>([]);\n\n /** The documents are in the workspace, and the check mark has been seen. */\n readonly completed = output<Workspace>();\n\n private readonly store = inject(WorkspacesStore);\n\n protected readonly status = signal<\"loading\" | \"ready\" | \"error\">(\"loading\");\n protected readonly busyId = signal<string | null>(null);\n protected readonly doneId = signal<string | null>(null);\n protected readonly error = signal<Message | null>(null);\n\n protected readonly creating = signal(false);\n /** The name of the workspace being made, in a form of its own. */\n private readonly newModel = signal({ name: \"\" });\n protected readonly newForm = form(this.newModel, path => maxLength(path.name, NEW_WORKSPACE_NAME_MAX));\n protected readonly newName = computed(() => this.newModel().name);\n protected readonly submitting = signal(false);\n /** The workspaces made from here, newest first: they head the list. */\n private readonly created = signal<string[]>([]);\n\n private readonly candidates = computed(() => {\n const from = this.fromWorkspaceId();\n return this.store.workspaces().filter(w => canWrite(w.currentUserRole) && w.id !== from);\n });\n protected readonly rows = computed(() => {\n const candidates = this.candidates();\n const heading = this.created()\n .map(id => candidates.find(w => w.id === id))\n .filter((w): w is Workspace => !!w);\n return [...heading, ...candidates.filter(w => !this.created().includes(w.id))];\n });\n protected readonly doneWorkspace = computed(() => this.store.workspaces().find(w => w.id === this.doneId()));\n\n /** Two workspaces with the same name cannot be told apart in a list: the name is not offered twice (whatever its case). */\n protected readonly duplicate = computed(() => {\n const name = this.newName().trim().toLowerCase();\n return !!name && this.store.workspaces().some(w => w.name.trim().toLowerCase() === name);\n });\n protected readonly canCreate = computed(() => !!this.newName().trim() && !this.duplicate());\n\n private timer: ReturnType<typeof setTimeout> | undefined;\n\n constructor() {\n // Once, when first shown: the store is shared and usually loaded already.\n void this.load();\n inject(DestroyRef).onDestroy(() => clearTimeout(this.timer));\n }\n\n protected async load(): Promise<void> {\n if (this.store.loaded()) {\n this.status.set(\"ready\");\n return;\n }\n this.status.set(\"loading\");\n try {\n await this.store.ensureLoaded();\n this.status.set(\"ready\");\n } catch {\n this.status.set(\"error\");\n }\n }\n\n protected isNew(workspace: Workspace): boolean {\n return this.created().includes(workspace.id);\n }\n\n protected async add(workspace: Workspace): Promise<void> {\n if (this.busyId() || this.doneId()) return;\n this.error.set(null);\n this.busyId.set(workspace.id);\n try {\n if (this.mode() === \"move\") {\n const from = this.fromWorkspaceId();\n const ids = this.documentIds();\n if (from && ids.length) await this.store.moveDocuments(from, workspace.id, [...ids]);\n } else if (this.documents().length) {\n await this.store.saveDocuments(workspace.id, [...this.documents()]);\n }\n } catch (error) {\n this.error.set(workspaceErrorMessage(error));\n return;\n } finally {\n this.busyId.set(null);\n }\n this.doneId.set(workspace.id);\n this.timer = setTimeout(() => this.completed.emit(workspace), this.mode() === \"move\" ? MOVE_FEEDBACK_MS : SAVE_FEEDBACK_MS);\n }\n\n protected onNameKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n void this.create();\n } else if (event.key === \"Escape\") {\n // Closes the form, not whatever is around the picker.\n event.stopPropagation();\n this.cancelNew();\n }\n }\n\n protected cancelNew(): void {\n if (this.submitting()) return;\n this.creating.set(false);\n this.newModel.set({ name: \"\" });\n this.error.set(null);\n }\n\n protected async create(): Promise<void> {\n if (this.submitting() || !this.canCreate()) return;\n this.submitting.set(true);\n this.error.set(null);\n try {\n const workspace = await this.store.create({ name: this.newName().trim() });\n this.created.update(ids => [workspace.id, ...ids]);\n this.creating.set(false);\n this.newModel.set({ name: \"\" });\n } catch (error) {\n this.error.set(workspaceErrorMessage(error));\n } finally {\n this.submitting.set(false);\n }\n }\n}\n","import { NgTemplateOutlet } from \"@angular/common\";\nimport { afterNextRender, ChangeDetectionStrategy, Component, computed, effect, inject, Injector, untracked, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n BreakpointObserverService,\n createCallable,\n DialogResult,\n DialogTitleComponent,\n injectCallRef,\n PopoverDirective,\n SheetBodyComponent,\n SheetComponent,\n SheetHeaderComponent\n} from \"@sinequa/galactik\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { PickerDocument, WorkspacePicker } from \"./workspace-picker\";\n\nexport interface WorkspacePickerDialogConfig {\n mode: \"save\" | \"move\";\n /** Where the popover opens from. Without one (or on a narrow window) it is a sheet. */\n anchor?: HTMLElement | null;\n /** `save`: the documents to attach, by name. */\n documents?: readonly PickerDocument[];\n /** `move`: where they come from, and which of its documents. */\n fromWorkspaceId?: string | null;\n documentIds?: readonly string[];\n}\n\n/** What the dialog resolves with once the documents are in: the workspace they went to. */\nexport type WorkspacePickerResult = { type: \"dialog-confirm\"; workspace: Workspace };\n\nlet nextId = 0;\n\n/**\n * \"Save to workspace\" / \"Move to workspace\", as a popover under the button that asked\n * (`anchor`), or a sheet at the bottom of a narrow screen.\n *\n * ```ts\n * const result = await PickWorkspace.call({ mode: \"save\", anchor: button, documents: [{ name: \"report.pdf\" }] });\n * ```\n *\n * The popover is galactik's `[popover]` — the browser's light dismiss closes it on an outside\n * press or Escape, and floating-ui keeps it under the anchor; the sheet is galactik's `Sheet`,\n * modal, with its backdrop. Which one is decided when it opens.\n */\n@Component({\n selector: \"workspace-picker-dialog\",\n imports: [NgTemplateOutlet, TranslocoPipe, DialogTitleComponent, PopoverDirective, SheetBodyComponent, SheetComponent, SheetHeaderComponent, WorkspacePicker],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ng-template #picker>\n <workspace-picker\n [mode]=\"config.mode\"\n [documents]=\"config.documents ?? []\"\n [fromWorkspaceId]=\"config.fromWorkspaceId ?? null\"\n [documentIds]=\"config.documentIds ?? []\"\n (completed)=\"complete($event)\" />\n </ng-template>\n\n @if (asPopover) {\n <div\n popover\n #popover=\"popover\"\n [id]=\"popoverId\"\n placement=\"bottom-end\"\n [offset]=\"8\"\n [matchTriggerWidth]=\"false\"\n class=\"w-[19rem] max-w-[calc(100vw-1rem)] rounded-(--radius-xl) border border-(--stroke-primary-lighter) bg-(--bg-neutral-white) p-4 text-(--font-neutral-black) shadow-[0_10px_15px_-3px_rgba(45,57,58,0.1),0_4px_6px_-2px_rgba(45,57,58,0.05)]\"\n [attr.aria-label]=\"title() | transloco\"\n (toggle)=\"onToggle($event)\">\n <h2 class=\"sr-only\">{{ title() | transloco }}</h2>\n <ng-container [ngTemplateOutlet]=\"picker\" />\n </div>\n } @else {\n <Sheet #sheet side=\"bottom\" [ariaLabel]=\"title() | transloco\" (closed)=\"onSheetClosed($event)\">\n <SheetHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\">\n <DialogTitle>{{ title() | transloco }}</DialogTitle>\n </SheetHeader>\n <SheetBody>\n <ng-container [ngTemplateOutlet]=\"picker\" />\n </SheetBody>\n </Sheet>\n }\n `\n})\nexport class WorkspacePickerDialog {\n protected readonly call = injectCallRef<WorkspacePickerDialogConfig, DialogResult>();\n private readonly injector = inject(Injector);\n\n protected readonly config = this.call.props();\n protected readonly title = computed(() =>\n this.config.mode === \"move\" ? \"documentWorkspaces.picker.moveToWorkspace\" : \"documentWorkspaces.picker.saveToWorkspace\"\n );\n\n /** A popover when there is an anchor to hang it from and room for it; a sheet otherwise. Decided once, when it opens. */\n protected readonly asPopover = !!this.config.anchor && !inject(BreakpointObserverService).isMobile();\n protected readonly popoverId = `workspace-picker-${nextId++}`;\n\n private readonly popover = viewChild<PopoverDirective>(\"popover\");\n private readonly sheet = viewChild<SheetComponent>(\"sheet\");\n\n private outcome: DialogResult | null = null;\n private ended = false;\n\n constructor() {\n // Opened once the view is there. Registered from an effect so that it runs after the hooks of the\n // directives inside the view (the popover's wiring listens for `toggle` in its own).\n effect(() => {\n const popover = this.popover();\n const sheet = this.sheet();\n if (!popover && !sheet) return;\n untracked(() =>\n afterNextRender(\n () => {\n if (popover) this.showPopover(popover);\n else sheet?.showModal();\n },\n { injector: this.injector }\n )\n );\n });\n }\n\n /**\n * The popover finds its trigger by `popovertarget`, so the host's button gets it for as long as the\n * popover is being placed. The native behaviour of such an attribute — toggling the popover on the\n * button's own click — is not wanted: what a press on the button means is the host's business.\n */\n private showPopover(popover: PopoverDirective): void {\n const anchor = this.config.anchor!;\n anchor.setAttribute(\"popovertarget\", this.popoverId);\n popover.show();\n // After the popover has read it (on its `toggle`, a task later), not before.\n setTimeout(() => anchor.removeAttribute(\"popovertarget\"), 0);\n }\n\n protected complete(workspace: Workspace): void {\n this.outcome = { type: \"dialog-confirm\", workspace } satisfies WorkspacePickerResult;\n const popover = this.popover();\n if (popover) popover.close();\n else this.sheet()?.close(\"dialog-confirm\");\n }\n\n /** The popover is closed — by the picker, an outside press, Escape: that is the end of the call. */\n protected onToggle(event: Event): void {\n if ((event as ToggleEvent).newState === \"closed\") this.end(\"dialog-cancel\");\n }\n\n protected onSheetClosed(event: DialogResult): void {\n this.end(event);\n }\n\n private end(event: DialogResult): void {\n if (this.ended) return;\n this.ended = true;\n this.call.end(this.outcome ?? event);\n }\n}\n\n/** Imperative entry point: `await PickWorkspace.call({ mode: \"save\", anchor, documents })`. */\nexport const PickWorkspace = createCallable<WorkspacePickerDialogConfig, DialogResult>(WorkspacePickerDialog);\n","import { ChangeDetectionStrategy, Component, inject, Injector, output } from \"@angular/core\";\nimport type { PickerDocument } from \"./workspace-picker\";\nimport { PickWorkspace, WorkspacePickerResult } from \"./workspace-picker.dialog\";\n\n/**\n * Opens the \"Save to / Move to workspace\" picker from a host page's own button, and\n * says what came of it. It draws nothing itself: it is what the `lexiq-workspace-picker`\n * custom element wraps, for hosts that have no Angular component to open the dialog from.\n * In Angular, call `PickWorkspace.call(...)` directly.\n */\n@Component({\n selector: \"workspace-picker-launcher\",\n template: \"\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"hidden\" }\n})\nexport class WorkspacePickerLauncher {\n /** Documents were saved to a workspace: its name. */\n readonly saved = output<string>();\n /** Documents were moved to another workspace. */\n readonly moved = output<void>();\n\n private readonly injector = inject(Injector);\n\n /** Opens \"Save to workspace\" under `anchor`. */\n async openSaveCard(anchor: HTMLElement, options: { documents?: readonly PickerDocument[] } = {}): Promise<void> {\n const result = await PickWorkspace.call({ mode: \"save\", anchor, documents: options.documents }, { injector: this.injector });\n if (typeof result === \"object\") this.saved.emit((result as WorkspacePickerResult).workspace.name);\n }\n\n /** Opens \"Move to workspace\" under `anchor`, for documents of the workspace they come from. */\n async openMoveMenu(anchor: HTMLElement, documentIds: readonly string[], options: { fromWorkspaceId: string }): Promise<void> {\n const result = await PickWorkspace.call({ mode: \"move\", anchor, fromWorkspaceId: options.fromWorkspaceId, documentIds }, { injector: this.injector });\n if (typeof result === \"object\") this.moved.emit();\n }\n}\n","import { ChangeDetectionStrategy, Component, inject, Injector, input, model, output } from \"@angular/core\";\nimport { provideTranslocoScope } from \"@jsverse/transloco\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { WorkspaceDetail } from \"../detail/workspace-detail\";\nimport { EditWorkspace, WorkspaceEditResult } from \"../detail/workspace-edit.dialog\";\nimport { WorkspaceList } from \"../list/workspace-list\";\nimport type { AskFollowUp } from \"../preview/document-preview-panel\";\nimport { UploadTracker } from \"../upload/upload-tracker\";\n\n/**\n * The whole workspaces view in one component: the list, a workspace with its documents\n * and preview, the dialogs they open, and the upload tracker.\n *\n * It is the piece that decides where things go — which the parts leave to their host:\n * choosing a workspace opens it, \"Add workspace\" opens the dialog and then the new\n * workspace, leaving a workspace goes back to the list. `workspaceId` is what is shown\n * (`null` is the list), two-way: a host can open a workspace, and is told which is open.\n *\n * ```html\n * <workspaces-shell uploadEndpoint=\"/tus\" [(workspaceId)]=\"id\" (askFollowUp)=\"chat.ask($event)\" />\n * ```\n *\n * The parts are exported as well (`WorkspaceList`, `WorkspaceDetail`…) for a host that\n * wants its own layout. The shell is also what the `lexiq-workspaces` custom element\n * wraps, for pages that are not Angular.\n */\n@Component({\n selector: \"workspaces-shell\",\n imports: [UploadTracker, WorkspaceDetail, WorkspaceList],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block h-full min-h-0 min-w-0 dark:bg-(--bg-neutral-base) dark:text-(--font-neutral-black)\", \"[class.dark]\": \"theme() === 'dark'\" },\n template: `\n @if (workspaceId(); as id) {\n <workspace-detail\n [workspaceId]=\"id\"\n [uploadEndpoint]=\"uploadEndpoint()\"\n [queryName]=\"queryName()\"\n (back)=\"workspaceId.set(null)\"\n (deleted)=\"workspaceId.set(null)\"\n (workspaceUpdated)=\"workspaceUpdated.emit($event)\"\n (askFollowUp)=\"askFollowUp.emit($event)\" />\n } @else {\n <workspace-list (opened)=\"workspaceId.set($event.id)\" (createRequested)=\"addWorkspace()\" />\n }\n <upload-tracker />\n `\n})\nexport class WorkspacesShell {\n /** The workspace shown; `null` for the list. */\n readonly workspaceId = model<string | null>(null);\n /** The tus endpoint uploads go to. */\n readonly uploadEndpoint = input(\"\");\n /** The query the preview web service renders a document for. */\n readonly queryName = input(\"_query\");\n /** `dark` turns the dark tokens on for the shell; otherwise it follows the page. */\n readonly theme = input<\"light\" | \"dark\" | null>(null);\n\n /** A workspace was created from here. */\n readonly workspaceCreated = output<Workspace>();\n /** A workspace was edited from here. */\n readonly workspaceUpdated = output<Workspace>();\n /** \"Ask follow-up\" on a previewed document. */\n readonly askFollowUp = output<AskFollowUp>();\n\n private readonly injector = inject(Injector);\n\n /** Opens the \"Add workspace\" dialog, then the workspace it made. */\n async addWorkspace(): Promise<void> {\n const result = await EditWorkspace.call({ mode: \"add\" }, { injector: this.injector });\n if (typeof result !== \"object\") return;\n const { workspace } = result as WorkspaceEditResult;\n this.workspaceCreated.emit(workspace);\n this.workspaceId.set(workspace.id);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sinequa-atomic-angular-document-workspaces.mjs","names":[],"sources":["../../../projects/atomic-angular/document-workspaces/utils/doc-type.ts","../../../projects/atomic-angular/document-workspaces/utils/workspace-color.ts","../../../projects/atomic-angular/document-workspaces/store/workspace-mappers.ts","../../../projects/atomic-angular/document-workspaces/store/workspaces.store.ts","../../../projects/atomic-angular/document-workspaces/upload/upload-tracker.store.ts","../../../projects/atomic-angular/document-workspaces/services/current-user.service.ts","../../../projects/atomic-angular/document-workspaces/utils/base-url.ts","../../../projects/atomic-angular/document-workspaces/utils/upload-jobs.ts","../../../projects/atomic-angular/document-workspaces/utils/uuid.ts","../../../projects/atomic-angular/document-workspaces/upload/session-recovery.ts","../../../projects/atomic-angular/document-workspaces/upload/status-poller.ts","../../../projects/atomic-angular/document-workspaces/upload/tus-upload.ts","../../../projects/atomic-angular/document-workspaces/upload/upload.service.ts","../../../projects/atomic-angular/document-workspaces/services/principal-directory.service.ts","../../../projects/atomic-angular/document-workspaces/directives/dropzone.directive.ts","../../../projects/atomic-angular/document-workspaces/directives/resize-handle.directive.ts","../../../projects/atomic-angular/document-workspaces/utils/workspace-role.ts","../../../projects/atomic-angular/document-workspaces/directives/writer-only.directive.ts","../../../projects/atomic-angular/document-workspaces/utils/text-match.ts","../../../projects/atomic-angular/document-workspaces/pipes/highlight-match.pipe.ts","../../../projects/atomic-angular/document-workspaces/utils/format.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-document-labels.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-online-status.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-selection.ts","../../../projects/atomic-angular/document-workspaces/utils/text-search.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-text-search.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-workspace-filter.ts","../../../projects/atomic-angular/document-workspaces/components/shared/highlighted-text.ts","../../../projects/atomic-angular/document-workspaces/components/shared/workspace-role-tag.ts","../../../projects/atomic-angular/document-workspaces/utils/error-message.ts","../../../projects/atomic-angular/document-workspaces/components/shared/confirm.dialog.ts","../../../projects/atomic-angular/document-workspaces/utils/message.ts","../../../projects/atomic-angular/document-workspaces/components/shared/workspace-message.ts","../../../projects/atomic-angular/document-workspaces/components/shared/document-status-badge.ts","../../../projects/atomic-angular/document-workspaces/components/shared/document-type-icon.ts","../../../projects/atomic-angular/document-workspaces/components/shared/workspace-icon.ts","../../../projects/atomic-angular/document-workspaces/components/upload/upload-file-list.ts","../../../projects/atomic-angular/document-workspaces/components/upload/upload.dialog.ts","../../../projects/atomic-angular/document-workspaces/components/upload/connection-banner.ts","../../../projects/atomic-angular/document-workspaces/components/shared/activity-indicators.ts","../../../projects/atomic-angular/document-workspaces/components/upload/upload-tracker-section.ts","../../../projects/atomic-angular/document-workspaces/components/upload/upload-tracker.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-live-refresh.ts","../../../projects/atomic-angular/document-workspaces/components/list/workspace-row.ts","../../../projects/atomic-angular/document-workspaces/components/list/workspace-card.ts","../../../projects/atomic-angular/document-workspaces/components/list/workspace-list.ts","../../../projects/atomic-angular/document-workspaces/components/detail/document-card.ts","../../../projects/atomic-angular/document-workspaces/components/detail/document-row.ts","../../../projects/atomic-angular/document-workspaces/utils/members.ts","../../../projects/atomic-angular/document-workspaces/components/share/principal-avatar.ts","../../../projects/atomic-angular/document-workspaces/components/share/role-menu.ts","../../../projects/atomic-angular/document-workspaces/components/share/member-row.ts","../../../projects/atomic-angular/document-workspaces/components/share/principal-picker.ts","../../../projects/atomic-angular/document-workspaces/components/share/share.dialog.ts","../../../projects/atomic-angular/document-workspaces/components/preview/document-preview-panel.ts","../../../projects/atomic-angular/document-workspaces/components/detail/workspace-edit.dialog.ts","../../../projects/atomic-angular/document-workspaces/components/detail/workspace-detail.ts","../../../projects/atomic-angular/document-workspaces/composables/inject-workspace-mention.ts","../../../projects/atomic-angular/document-workspaces/components/mention/has-content.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-new.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-rows.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-search.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-select-all.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-list.ts","../../../projects/atomic-angular/document-workspaces/components/mention/workspace-mention-tabs.ts","../../../projects/atomic-angular/document-workspaces/components/picker/workspace-picker.ts","../../../projects/atomic-angular/document-workspaces/components/picker/workspace-picker.dialog.ts","../../../projects/atomic-angular/document-workspaces/components/picker/workspace-picker-launcher.ts","../../../projects/atomic-angular/document-workspaces/components/shell/workspaces-shell.ts"],"sourcesContent":["import type { DocumentType } from \"../models/workspace.model\";\n\n/**\n * The document DTO carries no `type`, so it is derived from the file name.\n *\n * @param filename - A document file name, e.g. `report.pdf`.\n *\n * @returns The icon family the document belongs to.\n */\nexport function getDocumentType(filename: string): DocumentType {\n const ext = filename.slice(filename.lastIndexOf(\".\")).toLowerCase();\n if (ext === \".pdf\") return \"pdf\";\n if (ext === \".csv\" || ext === \".xlsx\") return \"csv\";\n return \"doc\";\n}\n","import type { WorkspaceColor } from \"../models/workspace.model\";\n\nconst PALETTE: readonly WorkspaceColor[] = [\"default\", \"teal\", \"pink\", \"blue\", \"orange\", \"indigo\", \"green\"];\n\n/**\n * The API has no colour field, so one is derived from the workspace id: the\n * same workspace always renders with the same accent across reloads and\n * reorders, without the server knowing anything about it.\n *\n * @param id - The workspace (container) id.\n */\nexport function workspaceColorFor(id: string): WorkspaceColor {\n let hash = 0;\n for (let i = 0; i < id.length; i++) {\n hash = (hash * 31 + id.charCodeAt(i)) | 0;\n }\n return PALETTE[Math.abs(hash) % PALETTE.length];\n}\n","// Wire shape (@sinequa/atomic's Workspace/WorkspaceDocument) -> view model\n// (Workspace/WorkspaceDocument of ../models). This is the only place the API\n// vocabulary meets the UI one.\nimport type { Workspace as WireWorkspace, WorkspaceDocument as WireWorkspaceDocument } from \"@sinequa/atomic\";\nimport type { Workspace, WorkspaceDocument } from \"../models/workspace.model\";\nimport { getDocumentType } from \"../utils/doc-type\";\nimport { workspaceColorFor } from \"../utils/workspace-color\";\n\nexport function mapWireWorkspaceToWorkspace(dto: WireWorkspace): Workspace {\n return {\n id: dto.id,\n name: dto.name,\n color: workspaceColorFor(dto.id),\n description: dto.description ?? \"\",\n tags: dto.tags ?? [],\n documentCount: dto.documentCount,\n creatorId: dto.creator,\n ownerIds: dto.owners ?? [],\n readerIds: dto.readers ?? [],\n currentUserRole: dto.currentUserRole\n };\n}\n\n/** Lifecycle status of a document as the documents listing spells it. */\nexport type WireDocumentStatus = \"uploading\" | \"ready_for_indexing\" | \"sent_for_indexing\" | \"indexing\" | \"indexed\" | \"integrity_failed\" | \"failed\";\n\n// The server sends status/percent/jobId on every document, but @sinequa/atomic\n// WorkspaceDocument declares none of the three. Drop this widening once\n// atomic declares them.\ntype WireWorkspaceDocumentExt = WireWorkspaceDocument & {\n status?: WireDocumentStatus;\n percent?: number;\n jobId?: string;\n};\n\n/**\n * Server lifecycle status -> the three-state badge the views render.\n * `indexed` maps to null (terminal success, no badge), and an absent or\n * unknown status maps to undefined so the document renders with no badge at\n * all rather than a wrong one.\n */\nexport function mapDocumentStatus(status?: string): WorkspaceDocument[\"status\"] {\n switch (status) {\n case \"uploading\":\n return \"uploading\";\n case \"ready_for_indexing\":\n case \"sent_for_indexing\":\n case \"indexing\":\n return \"indexing\";\n case \"indexed\":\n return null;\n case \"integrity_failed\":\n case \"failed\":\n return \"error\";\n default:\n return undefined;\n }\n}\n\n// size/source/uploadedBy have no server equivalent — left unset for\n// server-loaded documents. status/percent/jobId, by contrast, ARE served:\n// mapping them is what lets a document keep its badge across a reload or a\n// re-open of the workspace.\nexport function mapWireDocumentToWorkspaceDocument(dto: WireWorkspaceDocument): WorkspaceDocument {\n const wire = dto as WireWorkspaceDocumentExt;\n return {\n id: dto.id,\n name: dto.name,\n workspaceId: dto.workspaceId,\n type: getDocumentType(dto.name),\n // The swagger makes it nullable (\"or `null` when unknown\") although atomic\n // declares a plain string: normalise so the model's optional means \"unknown\".\n createdAt: dto.createdAt ?? undefined,\n status: mapDocumentStatus(wire.status),\n percent: wire.percent,\n jobId: wire.jobId\n };\n}\n","import { computed } from \"@angular/core\";\nimport { withDevtools } from \"@ngrx-toolkit/core\";\nimport { patchState, signalStore, withComputed, withMethods, withState } from \"@ngrx/signals\";\nimport {\n addWorkspaceDocuments,\n createWorkspace,\n deleteWorkspaceDocuments,\n deleteWorkspaces,\n getWorkspace,\n listWorkspaceDocuments,\n listWorkspaces,\n updateWorkspace\n} from \"@sinequa/atomic\";\nimport type { UpdateWorkspaceInput } from \"@sinequa/atomic\";\nimport type { Workspace, WorkspaceDocument } from \"../models/workspace.model\";\nimport { mapWireDocumentToWorkspaceDocument, mapWireWorkspaceToWorkspace } from \"./workspace-mappers\";\n\n/** API maximum page size. */\nconst PAGE_SIZE = 100;\n\n/**\n * Most workspaces the list keeps loaded: \"load more\" stops there, so the list never puts an\n * unbounded number of cards in the DOM. Past it, {@link WorkspacesStore.truncated} is true.\n */\nexport const MAX_LOADED_WORKSPACES = 3 * PAGE_SIZE;\n\nexport interface Pagination {\n page: number;\n total: number;\n}\n\nexport interface WorkspacesState {\n workspaces: Workspace[];\n /** True once the first page of workspaces has been read from the server. */\n loaded: boolean;\n /** True while a (re)load is in flight. */\n loading: boolean;\n pagination: Pagination;\n documents: Record<string, WorkspaceDocument[]>;\n documentPagination: Record<string, Pagination>;\n /** Ids ticked in the \"+\"/\"@\" mention pickers — shared between every picker instance. */\n selectedIds: string[];\n}\n\nexport interface CreateWorkspaceInput {\n name: string;\n description?: string;\n tags?: string[];\n /** Optional: they go out on the same POST as the metadata, so sharing at creation costs no extra round trip. */\n owners?: string[];\n readers?: string[];\n}\n\nexport interface RemoveWorkspacesResult {\n succeeded: string[];\n failed: { id: string; error: string }[];\n}\n\nconst initialState: WorkspacesState = {\n workspaces: [],\n loaded: false,\n loading: false,\n pagination: { page: 0, total: 0 },\n documents: {},\n documentPagination: {},\n selectedIds: []\n};\n\n/** Returns `workspaces` with the document count of `id` moved by `delta`. */\nfunction withDocumentCountAdjusted(workspaces: Workspace[], id: string, delta: number): Workspace[] {\n return workspaces.map(w => (w.id === id ? { ...w, documentCount: Math.max(0, w.documentCount + delta) } : w));\n}\n\n/**\n * Workspaces and their documents, backed by the container-management API\n * (`@sinequa/atomic`'s `*Container*` functions).\n *\n * Root-scoped on purpose: the workspace list, the mention picker and the\n * save/move picker are separate components that have to see the same cache and\n * the same selection.\n *\n * Every write method resolves or rejects around the matching endpoint and only\n * touches the cache once the server has answered, so a rejected call leaves\n * the state as it was — the call site reports the error.\n *\n * `addDocuments`, `discardDocuments` and `patchDocument` stay purely local. The upload\n * flow no longer uses them: the list is the index, and only the listing writes to it.\n */\nexport const WorkspacesStore = signalStore(\n { providedIn: \"root\" },\n withDevtools(\"Workspaces\"),\n\n withState(initialState),\n\n withComputed(({ workspaces, pagination, selectedIds }) => ({\n hasMore: computed(() => workspaces().length < pagination().total && workspaces().length < MAX_LOADED_WORKSPACES),\n /** The server holds more workspaces than the list is allowed to load. */\n truncated: computed(() => workspaces().length < pagination().total && workspaces().length >= MAX_LOADED_WORKSPACES),\n selectedCount: computed(() => selectedIds().length),\n selectedIdSet: computed(() => new Set(selectedIds()))\n })),\n\n withMethods(store => {\n let loadingPromise: Promise<Workspace[]> | null = null;\n let loadingMorePromise: Promise<Workspace[]> | null = null;\n\n const setDocuments = (workspaceId: string, documents: WorkspaceDocument[]) => {\n patchState(store, state => ({ documents: { ...state.documents, [workspaceId]: documents } }));\n };\n\n // Counts are moved by a delta rather than reset to the cached list's length:\n // the cache holds at most the pages loaded so far, while `documentCount` is\n // the server's total.\n // The same goes for the documents' pagination total, which `hasMoreDocuments` compares\n // to the cached list: left as it was loaded, a workspace emptied of its documents would\n // still offer to load more.\n const adjustDocumentCount = (workspaceId: string, delta: number) => {\n patchState(store, state => {\n const pagination = state.documentPagination[workspaceId];\n return {\n workspaces: withDocumentCountAdjusted(state.workspaces, workspaceId, delta),\n documentPagination: pagination\n ? { ...state.documentPagination, [workspaceId]: { ...pagination, total: Math.max(0, pagination.total + delta) } }\n : state.documentPagination\n };\n });\n };\n\n const loadDocuments = async (workspaceId: string, signal?: AbortSignal): Promise<WorkspaceDocument[]> => {\n const res = await listWorkspaceDocuments(workspaceId, { page: 1, pageSize: PAGE_SIZE }, { signal });\n const documents = res.documents.map(mapWireDocumentToWorkspaceDocument);\n patchState(store, state => ({\n documentPagination: { ...state.documentPagination, [workspaceId]: { page: res.page, total: res.total } }\n }));\n setDocuments(workspaceId, documents);\n return documents;\n };\n\n // Deleting everything that was loaded does not mean the workspace is empty: the server may hold\n // pages that were never loaded. Show them rather than an empty state next to a \"load more\".\n // The deletion already succeeded, so a failed refill is not an error here: the cache stays empty\n // and \"load more\" (which then asks for the first page) lets the user retry.\n const refillIfEmptied = async (workspaceId: string) => {\n if ((store.documents()[workspaceId] ?? []).length || !(store.documentPagination()[workspaceId]?.total ?? 0)) return;\n try {\n await loadDocuments(workspaceId);\n } catch {\n // see above\n }\n };\n\n const load = async (): Promise<Workspace[]> => {\n patchState(store, { loading: true });\n try {\n const res = await listWorkspaces({ page: 1, pageSize: PAGE_SIZE });\n patchState(store, {\n workspaces: res.workspaces.map(mapWireWorkspaceToWorkspace),\n pagination: { page: res.page, total: res.total },\n loaded: true\n });\n return store.workspaces();\n } finally {\n patchState(store, { loading: false });\n loadingPromise = null;\n }\n };\n\n return {\n /** The cached workspace, or `undefined`. Reactive when read from a template or a `computed`. */\n workspace(id: string): Workspace | undefined {\n return store.workspaces().find(w => w.id === id);\n },\n\n /** The cached documents of a workspace (empty until {@link loadDocuments} ran). */\n documentsOf(workspaceId: string): WorkspaceDocument[] {\n return store.documents()[workspaceId] ?? [];\n },\n\n hasMoreDocuments(workspaceId: string): boolean {\n const pagination = store.documentPagination()[workspaceId];\n return !!pagination && (store.documents()[workspaceId]?.length ?? 0) < pagination.total;\n },\n\n /**\n * Idempotent: every entry point that needs the list calls it; only the\n * first caller triggers a real fetch, the rest await the same in-flight promise.\n */\n ensureLoaded(): Promise<Workspace[]> {\n if (store.loaded()) return Promise.resolve(store.workspaces());\n loadingPromise ??= load();\n return loadingPromise;\n },\n\n /**\n * Unlike {@link ensureLoaded}, always goes back to the server — so\n * workspaces created by others appear and document counts are current when\n * coming back from a workspace's detail. Shares an in-flight load. A\n * failure leaves the cache as it was.\n */\n reload(): Promise<Workspace[]> {\n loadingPromise ??= load();\n return loadingPromise;\n },\n\n /** Shares an in-flight call, so a double click does not fetch (and append) the same page twice. */\n loadMore(): Promise<Workspace[]> {\n if (!store.hasMore()) return Promise.resolve(store.workspaces());\n loadingMorePromise ??= (async () => {\n try {\n const current = store.workspaces();\n // Deleting workspaces shifts the server's pages: `page + 1` would skip as many as were\n // deleted since. Ask for the page holding the first one not loaded yet (it may overlap\n // the loaded tail, hence the dedup).\n const page = Math.min(store.pagination().page + 1, Math.floor(current.length / PAGE_SIZE) + 1);\n const res = await listWorkspaces({ page, pageSize: PAGE_SIZE });\n patchState(store, state => {\n const loaded = new Set(state.workspaces.map(w => w.id));\n const fresh = res.workspaces.map(mapWireWorkspaceToWorkspace).filter(w => !loaded.has(w.id));\n return { workspaces: [...state.workspaces, ...fresh], pagination: { page: res.page, total: res.total } };\n });\n return store.workspaces();\n } finally {\n loadingMorePromise = null;\n }\n })();\n return loadingMorePromise;\n },\n\n /**\n * Fallback for a deep link opened before the list was loaded, or for an id\n * that is not on the loaded page: fetches the workspace directly instead\n * of assuming it is cached.\n */\n async fetch(id: string): Promise<Workspace> {\n const workspace = mapWireWorkspaceToWorkspace(await getWorkspace(id));\n patchState(store, state => ({\n workspaces: state.workspaces.some(w => w.id === id) ? state.workspaces.map(w => (w.id === id ? workspace : w)) : [...state.workspaces, workspace]\n }));\n return workspace;\n },\n\n async create({ name, description = \"\", tags = [], owners = [], readers = [] }: CreateWorkspaceInput): Promise<Workspace> {\n const workspace = mapWireWorkspaceToWorkspace(await createWorkspace({ name, description, tags, owners, readers }));\n patchState(store, state => ({\n workspaces: [...state.workspaces, workspace],\n // The server's total grew by one too: left alone, a full list would offer to load a phantom workspace.\n pagination: { ...state.pagination, total: state.pagination.total + 1 },\n documents: { ...state.documents, [workspace.id]: [] }\n }));\n return workspace;\n },\n\n /**\n * `patch` is wire-shaped (the body of `PUT .../container/{id}`), not\n * Workspace-shaped: callers changing owners/readers always send the two\n * full arrays, since the API replaces them rather than patching deltas.\n *\n * @returns The updated workspace, or `null` when `id` is not in the cache.\n */\n async update(id: string, patch: UpdateWorkspaceInput): Promise<Workspace | null> {\n if (!store.workspaces().some(w => w.id === id)) return null;\n const updated = mapWireWorkspaceToWorkspace(await updateWorkspace(id, patch));\n patchState(store, state => ({ workspaces: state.workspaces.map(w => (w.id === id ? updated : w)) }));\n return updated;\n },\n\n /**\n * Always a bulk call, even for a single id — the API only exposes a bulk\n * DELETE. Only the ids the server confirmed deleted are dropped from the\n * cache; a partial failure leaves the rest in place for the call site to report.\n */\n async remove(ids: string[]): Promise<RemoveWorkspacesResult> {\n const result = await deleteWorkspaces(ids);\n const succeeded = new Set(result.succeeded);\n patchState(store, state => ({\n workspaces: state.workspaces.filter(w => !succeeded.has(w.id)),\n // Followed like the documents' total: an emptied list must not offer to load more.\n pagination: { ...state.pagination, total: Math.max(0, state.pagination.total - result.succeeded.length) },\n documents: Object.fromEntries(Object.entries(state.documents).filter(([id]) => !succeeded.has(id))),\n selectedIds: state.selectedIds.filter(id => !succeeded.has(id))\n }));\n return { succeeded: result.succeeded, failed: result.failed.map(f => ({ id: f.workspaceId, error: f.error })) };\n },\n\n /**\n * Local only — for when a read already proved a workspace is gone from the\n * server (a 404), rather than this session deleting it: the detail view\n * calls this once the user actually leaves a workspace its background\n * refresh found deleted, instead of {@link remove}, which would call\n * DELETE on something already deleted. Kept in the cache until then, so\n * the view can still show its name while it explains what happened.\n */\n forget(id: string): void {\n patchState(store, state => ({\n workspaces: state.workspaces.filter(w => w.id !== id),\n pagination: state.workspaces.some(w => w.id === id) ? { ...state.pagination, total: Math.max(0, state.pagination.total - 1) } : state.pagination,\n documents: Object.fromEntries(Object.entries(state.documents).filter(([wid]) => wid !== id)),\n documentPagination: Object.fromEntries(Object.entries(state.documentPagination).filter(([wid]) => wid !== id)),\n selectedIds: state.selectedIds.filter(selected => selected !== id)\n }));\n },\n\n loadDocuments,\n\n /** Without this, a workspace with more than one page of documents is silently truncated. */\n async loadMoreDocuments(workspaceId: string): Promise<WorkspaceDocument[]> {\n const current = store.documents()[workspaceId] ?? [];\n const pagination = store.documentPagination()[workspaceId];\n if (!pagination || current.length >= pagination.total) return current;\n // Deleting documents shifts the server's pages: `pagination.page + 1` would skip as many\n // documents as were deleted since. Ask for the page holding the first one not loaded yet\n // (it may overlap the loaded tail, hence the dedup).\n const page = Math.min(pagination.page + 1, Math.floor(current.length / PAGE_SIZE) + 1);\n const res = await listWorkspaceDocuments(workspaceId, { page, pageSize: PAGE_SIZE });\n const loaded = new Set(current.map(d => d.id));\n const fresh = res.documents.map(mapWireDocumentToWorkspaceDocument).filter(d => !loaded.has(d.id));\n const merged = [...current, ...fresh];\n patchState(store, state => ({\n documentPagination: { ...state.documentPagination, [workspaceId]: { page: res.page, total: res.total } }\n }));\n setDocuments(workspaceId, merged);\n return merged;\n },\n\n /**\n * Local only: adds documents to the cache without calling the API. The upload\n * flow does not use it (a file enters the list once the listing returns it).\n */\n addDocuments(workspaceId: string, documents: WorkspaceDocument[]): void {\n setDocuments(workspaceId, [...(store.documents()[workspaceId] ?? []), ...documents]);\n adjustDocumentCount(workspaceId, documents.length);\n },\n\n /**\n * Local only, the counterpart of {@link addDocuments}: forgets documents without\n * calling the API.\n * `removeDocuments` is the one that deletes on the server.\n */\n discardDocuments(workspaceId: string, documentIds: string[]): void {\n const before = store.documents()[workspaceId] ?? [];\n const after = before.filter(d => !documentIds.includes(d.id));\n setDocuments(workspaceId, after);\n adjustDocumentCount(workspaceId, after.length - before.length);\n },\n\n /** Local only: merges a patch into one cached document, matched by id. */\n patchDocument(workspaceId: string, documentId: string, patch: Partial<WorkspaceDocument>): void {\n patchState(store, state => ({\n documents: {\n ...state.documents,\n [workspaceId]: (state.documents[workspaceId] ?? []).map(d => (d.id === documentId ? { ...d, ...patch } : d))\n }\n }));\n },\n\n /**\n * \"Save to workspace\": attaches an already-existing document (no file\n * transfer) to a workspace. The backend contract for this is provisional.\n */\n async saveDocuments(workspaceId: string, documents: Pick<WorkspaceDocument, \"name\">[]): Promise<WorkspaceDocument[]> {\n const created = await addWorkspaceDocuments(\n workspaceId,\n documents.map(d => ({ name: d.name }))\n );\n const mapped = created.map(mapWireDocumentToWorkspaceDocument);\n setDocuments(workspaceId, [...(store.documents()[workspaceId] ?? []), ...mapped]);\n adjustDocumentCount(workspaceId, mapped.length);\n return mapped;\n },\n\n async removeDocuments(workspaceId: string, documentIds: string[]): Promise<void> {\n await deleteWorkspaceDocuments(workspaceId, documentIds);\n const before = store.documents()[workspaceId] ?? [];\n const after = before.filter(d => !documentIds.includes(d.id));\n setDocuments(workspaceId, after);\n adjustDocumentCount(workspaceId, after.length - before.length);\n await refillIfEmptied(workspaceId);\n },\n\n /**\n * \"Move to workspace\". There is no dedicated re-parent endpoint, so this\n * attaches a copy in the target first and only deletes the originals from\n * the source once that succeeded: a failed attach leaves the source\n * untouched instead of losing the documents.\n */\n async moveDocuments(fromWorkspaceId: string, toWorkspaceId: string, documentIds: string[]): Promise<void> {\n const from = store.documents()[fromWorkspaceId] ?? [];\n const moved = from.filter(d => documentIds.includes(d.id));\n if (!moved.length) return;\n\n const created = await addWorkspaceDocuments(\n toWorkspaceId,\n moved.map(d => ({ name: d.name }))\n );\n setDocuments(toWorkspaceId, [...(store.documents()[toWorkspaceId] ?? []), ...created.map(mapWireDocumentToWorkspaceDocument)]);\n adjustDocumentCount(toWorkspaceId, created.length);\n\n await deleteWorkspaceDocuments(fromWorkspaceId, documentIds);\n const remaining = from.filter(d => !documentIds.includes(d.id));\n setDocuments(fromWorkspaceId, remaining);\n adjustDocumentCount(fromWorkspaceId, remaining.length - from.length);\n await refillIfEmptied(fromWorkspaceId);\n },\n\n toggleSelected(id: string, checked: boolean): void {\n patchState(store, state => {\n const without = state.selectedIds.filter(selected => selected !== id);\n return { selectedIds: checked ? [...without, id] : without };\n });\n },\n\n clearSelection(): void {\n patchState(store, { selectedIds: [] });\n }\n };\n })\n);\n","import { computed } from \"@angular/core\";\nimport { withDevtools } from \"@ngrx-toolkit/core\";\nimport { patchState, signalStore, withComputed, withMethods, withState } from \"@ngrx/signals\";\nimport type { UploadJobStatus } from \"@sinequa/atomic\";\nimport type { ConnectionNotice, TrackedUpload, TrackFileInput, UploadStatus } from \"../models/upload.model\";\n\nexport interface TrackerWorkspace {\n id: string;\n name: string;\n}\n\nexport interface UploadTrackerState {\n uploads: TrackedUpload[];\n /** The workspaces being uploaded to, in the order they were first seen. */\n workspaces: TrackerWorkspace[];\n connection: ConnectionNotice | null;\n /** Files dropped from the tracker because their whole workspace finished — they still count as indexed. */\n clearedCount: number;\n /** What the server last said about each job's indexing, 0-100: indexed files / files of the job. */\n jobIndexing: Record<string, number>;\n /**\n * Jobs the user got rid of — closed, cancelled, cleared. The status API keeps listing a\n * job for a while after it ends, so without this a job closed by hand would come\n * straight back as an upload started elsewhere. Survives `reset()`, on purpose.\n */\n ignoredJobs: Record<string, true>;\n}\n\nconst initialState: UploadTrackerState = {\n uploads: [],\n workspaces: [],\n connection: null,\n clearedCount: 0,\n jobIndexing: {},\n ignoredJobs: {}\n};\n\n/** A job the status API reports that this client did not start — or no longer remembers starting. */\nexport interface RemoteJobEntry {\n job: UploadJobStatus;\n workspaceId: string;\n workspaceName: string;\n /** The current user started the job (`initiatedBy` is the current user's id). */\n mine: boolean;\n}\n\nconst FINISHED: readonly UploadStatus[] = [\"done\", \"error\", \"rejected\"];\nconst FAILED: readonly UploadStatus[] = [\"error\", \"rejected\"];\n\nexport const isFinished = (upload: TrackedUpload) => FINISHED.includes(upload.status);\nexport const isFailed = (upload: TrackedUpload) => FAILED.includes(upload.status);\n\n/** The job an upload belongs to: ids are `<jobId>/<fileName>`. */\nconst jobOf = (upload: TrackedUpload) => upload.id.slice(0, upload.id.indexOf(\"/\"));\n\nexport interface PhaseProgress {\n uploading: number;\n indexing: number;\n}\n\n/**\n * How far along a set of uploads is, for each of the two phases, as one 0-100 number.\n *\n * Measured over the files of every job that still has something moving in the phase —\n * and over ALL of that job's files (a failed or refused one aside), not just the ones\n * currently listed in the phase. A file that moved on is a file that finished the phase:\n * counting only the ones left would make the bar drop, or stall, each time one finishes.\n * That is also how the server counts (`indexed files / files of the job`), so the two agree.\n *\n * - uploading: the files still sending count their own percentage, the others are at 100;\n * - indexing: the API has no per-file percentage, only the job's aggregate. What the\n * server last said and what is already known locally (files done / files) are both\n * lower bounds of the truth: the larger one wins, so the bar never falls back.\n */\nexport function phaseProgress(uploads: readonly TrackedUpload[], jobIndexing: Record<string, number>): PhaseProgress {\n const jobs = new Map<string, TrackedUpload[]>();\n for (const upload of uploads) {\n if (isFailed(upload)) continue;\n const files = jobs.get(jobOf(upload));\n if (files) files.push(upload);\n else jobs.set(jobOf(upload), [upload]);\n }\n\n let uploadingSum = 0;\n let uploadingFiles = 0;\n let indexingSum = 0;\n let indexingFiles = 0;\n for (const [jobId, files] of jobs) {\n if (files.some(f => f.status === \"uploading\")) {\n uploadingSum += files.reduce((sum, f) => sum + (f.status === \"uploading\" ? f.percent : 100), 0);\n uploadingFiles += files.length;\n }\n if (files.some(f => f.status === \"indexing\")) {\n const known = (files.filter(f => f.status === \"done\").length / files.length) * 100;\n indexingSum += Math.max(jobIndexing[jobId] ?? 0, known) * files.length;\n indexingFiles += files.length;\n }\n }\n return {\n uploading: uploadingFiles ? Math.round(uploadingSum / uploadingFiles) : 0,\n indexing: indexingFiles ? Math.round(indexingSum / indexingFiles) : 0\n };\n}\n\nfunction updateOne(uploads: TrackedUpload[], id: string, change: (upload: TrackedUpload) => TrackedUpload): TrackedUpload[] {\n return uploads.map(upload => (upload.id === id ? change(upload) : upload));\n}\n\n/** `ignored` plus the jobs of `uploads`. */\nfunction withIgnored(ignored: Record<string, true>, uploads: readonly TrackedUpload[]): Record<string, true> {\n const next = { ...ignored };\n for (const upload of uploads) next[jobOf(upload)] = true;\n return next;\n}\n\n/** The row standing for a whole job, whose files the API does not list: `<jobId>/`. */\nconst remoteJobRowId = (jobId: string) => `${jobId}/`;\n\n/** Where a whole job stands, as one row: what the API says of its two phases. */\nfunction remoteJobState(job: UploadJobStatus): Pick<TrackedUpload, \"status\" | \"percent\"> {\n if (job.uploadStatus === \"uploading\") return { status: \"uploading\", percent: job.uploadProgress?.percentage ?? 0 };\n if (job.uploadStatus === \"upload_interrupted\") return { status: \"error\", percent: 0 };\n if (job.indexingStatus === \"complete\") return { status: \"done\", percent: 100 };\n if (job.indexingStatus === \"failed\" || job.indexingStatus === \"interrupted\") return { status: \"error\", percent: 0 };\n // The job's aggregate indexing percentage lives in `jobIndexing`: no file has its own.\n return { status: \"indexing\", percent: 0 };\n}\n\n/**\n * The rows a job started elsewhere shows as: one per file the API names (errors and\n * rejections, the only per-file data it has), and one for the job itself — unless a\n * named file already explains why it failed.\n */\nfunction remoteRows({ job, workspaceId, mine }: RemoteJobEntry): TrackedUpload[] {\n const base = { type: \"doc\", workspaceId, retryable: false, percent: 0, remote: { mine } } as const;\n const files: TrackedUpload[] = [\n ...(job.errors ?? []).map(e => ({ ...base, id: `${job.jobId}/${e.fileName}`, name: e.fileName, status: \"error\" as const, message: e.error ?? undefined })),\n ...(job.rejected ?? []).map(r => ({ ...base, id: `${job.jobId}/${r.fileName}`, name: r.fileName, status: \"rejected\" as const, message: r.reason }))\n ];\n const state = remoteJobState(job);\n if (state.status === \"error\" && files.length) return files;\n return [{ ...base, ...state, id: remoteJobRowId(job.jobId), name: \"\" }, ...files];\n}\n\nconst sameRow = (a: TrackedUpload, b: TrackedUpload) =>\n a.status === b.status && a.percent === b.percent && a.message === b.message && a.workspaceId === b.workspaceId && a.remote?.mine === b.remote?.mine;\n\n/**\n * The state of every upload the tracker follows — pure state, no I/O.\n *\n * `UploadService` writes to it as bytes move and as the server reports; the\n * tracker panel only reads it. Because nothing here talks to the network, a\n * test or a demo page can drive the panel by injecting this store and calling\n * `track()` / `setProgress()` / `setDone()`… directly.\n *\n * Root-scoped: an upload must outlive the page that started it.\n */\nexport const UploadTrackerStore = signalStore(\n { providedIn: \"root\" },\n withDevtools(\"UploadTracker\"),\n\n withState(initialState),\n\n withComputed(({ uploads, clearedCount, jobIndexing }) => ({\n /** Whether there is anything to show — the panel is hidden otherwise. */\n active: computed(() => uploads().length > 0),\n failed: computed(() => uploads().filter(isFailed)),\n byStatus: computed(() => {\n const groups: Record<UploadStatus, TrackedUpload[]> = { uploading: [], indexing: [], done: [], error: [], rejected: [] };\n for (const upload of uploads()) groups[upload.status].push(upload);\n return groups;\n }),\n /** Everything is over and nothing failed: the panel celebrates, then dismisses itself. */\n allDone: computed(() => uploads().length > 0 && uploads().every(u => u.status === \"done\")),\n /** Everything is over, whether it worked or not: nothing moves any more, and the panel may be closed. */\n settled: computed(() => uploads().length > 0 && uploads().every(isFinished)),\n /**\n * A failure on an upload of the current user's. It is theirs to read and close: the panel\n * stays until they do. A failure on someone else's upload is only worth the usual delay.\n */\n authorFailed: computed(() => uploads().some(u => isFailed(u) && (!u.remote || u.remote.mine))),\n /** Per workspace, how far each phase is — what the tracker's progress bars show. */\n progress: computed(() => {\n const byWorkspace = new Map<string, TrackedUpload[]>();\n for (const upload of uploads()) {\n const own = byWorkspace.get(upload.workspaceId);\n if (own) own.push(upload);\n else byWorkspace.set(upload.workspaceId, [upload]);\n }\n const progress: Record<string, PhaseProgress> = {};\n for (const [workspaceId, own] of byWorkspace) progress[workspaceId] = phaseProgress(own, jobIndexing());\n return progress;\n }),\n /** Every file ever tracked that got indexed — the ones already cleared plus the ones still listed. */\n indexedCount: computed(() => clearedCount() + uploads().filter(u => u.status === \"done\").length)\n })),\n\n withMethods(store => ({\n /** Starts following files that are being sent to a workspace. Re-tracking a known id restarts it. */\n track(files: TrackFileInput[], workspaceId: string, workspaceName: string): void {\n patchState(store, state => {\n const ids = new Set(files.map(f => f.id));\n const added: TrackedUpload[] = files.map(f => ({\n id: f.id,\n name: f.name,\n type: f.type,\n workspaceId,\n status: \"uploading\",\n percent: 0,\n retryable: false\n }));\n const known = state.workspaces.some(w => w.id === workspaceId);\n return {\n uploads: [...state.uploads.filter(u => !ids.has(u.id)), ...added],\n workspaces: known\n ? state.workspaces.map(w => (w.id === workspaceId ? { ...w, name: workspaceName } : w))\n : [...state.workspaces, { id: workspaceId, name: workspaceName }]\n };\n });\n },\n\n setProgress(id: string, percent: number): void {\n patchState(store, state => ({ uploads: updateOne(state.uploads, id, u => ({ ...u, percent })) }));\n },\n\n /** The bytes are in; indexing is now the server's business. */\n setIndexing(id: string): void {\n patchState(store, state => ({\n uploads: updateOne(state.uploads, id, u => ({ ...u, status: \"indexing\", percent: 0, message: undefined, retryable: false }))\n }));\n },\n\n setDone(id: string): void {\n patchState(store, state => ({\n uploads: updateOne(state.uploads, id, u => ({ ...u, status: \"done\", percent: 100, message: undefined, retryable: false }))\n }));\n },\n\n setError(id: string, options: { message?: string; retryable?: boolean } = {}): void {\n patchState(store, state => ({\n uploads: updateOne(state.uploads, id, u => ({ ...u, status: \"error\", percent: 0, message: options.message, retryable: options.retryable ?? false }))\n }));\n },\n\n /** A failed transfer is being sent again. */\n restart(id: string): void {\n patchState(store, state => ({\n uploads: updateOne(state.uploads, id, u => ({ ...u, status: \"uploading\", percent: 0, message: undefined, retryable: false }))\n }));\n },\n\n /**\n * Feeds in the job-level half of the status API: the per-file error\n * messages and rejections only `GET .../agent-workspace/status` reports,\n * and the job's aggregate indexing percentage (kept per job: no file has its own).\n *\n * Ids are `<jobId>/<fileName>` — the job and name the API reports errors\n * and rejections under — so they map back with a plain lookup.\n *\n * @returns Whether anything changed, which paces the poller.\n */\n applyJobs(jobs: UploadJobStatus[]): boolean {\n let next = store.uploads();\n let jobIndexing = store.jobIndexing();\n let changed = false;\n const change = (id: string, edit: (u: TrackedUpload) => TrackedUpload | null) => {\n next = next.map(u => {\n if (u.id !== id) return u;\n const edited = edit(u);\n if (!edited) return u;\n changed = true;\n return edited;\n });\n };\n\n for (const job of jobs) {\n for (const e of job.errors ?? []) {\n const message = e.error ?? undefined;\n change(`${job.jobId}/${e.fileName}`, u =>\n u.status !== \"error\" || u.message !== message ? { ...u, status: \"error\", percent: 0, message, retryable: false } : null\n );\n }\n for (const r of job.rejected ?? []) {\n change(`${job.jobId}/${r.fileName}`, u =>\n u.status !== \"rejected\" || u.message !== r.reason ? { ...u, status: \"rejected\", percent: 0, message: r.reason, retryable: false } : null\n );\n }\n // The job is over: whatever is still indexing in it (and was neither refused nor\n // failed, handled above) made it. This does not depend on the document listing,\n // whose ids differ from ours and which is only read for the open workspace.\n const complete = job.indexingStatus === \"complete\";\n if (complete) {\n next = next.map(u => {\n if (u.status !== \"indexing\" || !u.id.startsWith(`${job.jobId}/`)) return u;\n changed = true;\n return { ...u, status: \"done\", percent: 100, message: undefined, retryable: false };\n });\n }\n\n // The aggregate is the finest grain the API has: it moves one file at a time (indexed / total).\n // A finished job reports 0 on some servers, so completeness is read from the status, not the number.\n // Only jobs we follow are kept: the status endpoint also lists every older job of the workspace.\n const percent = complete ? 100 : job.indexingStatus === \"indexing\" ? job.indexingProgress?.percentage : undefined;\n if (typeof percent === \"number\" && jobIndexing[job.jobId] !== percent && next.some(u => u.id.startsWith(`${job.jobId}/`))) {\n jobIndexing = { ...jobIndexing, [job.jobId]: percent };\n changed = true;\n }\n }\n\n if (changed) patchState(store, { uploads: next, jobIndexing });\n return changed;\n },\n\n /**\n * Feeds in the jobs the status API reports that this client is not following — the ones\n * another user started, or its own from before a page reload. Each becomes a row (see\n * `remoteRows`), updated on every call; a job this client sent itself is left to the\n * local rows, and so is one the user already got rid of.\n *\n * @returns Whether anything changed, which paces the poller.\n */\n applyRemoteJobs(entries: RemoteJobEntry[]): boolean {\n const current = store.uploads();\n const ignored = store.ignoredJobs();\n const ownJobs = new Set(current.filter(u => !u.remote).map(jobOf));\n const rows = new Map(current.map(u => [u.id, u]));\n const workspaces = [...store.workspaces()];\n let changed = false;\n\n for (const entry of entries) {\n if (ignored[entry.job.jobId] || ownJobs.has(entry.job.jobId)) continue;\n for (const row of remoteRows(entry)) {\n const known = rows.get(row.id);\n if (known && sameRow(known, row)) continue;\n rows.set(row.id, known ? { ...known, ...row } : row);\n changed = true;\n }\n if (!workspaces.some(w => w.id === entry.workspaceId)) {\n workspaces.push({ id: entry.workspaceId, name: entry.workspaceName });\n changed = true;\n }\n }\n\n if (changed) patchState(store, { uploads: [...rows.values()], workspaces });\n return changed;\n },\n\n setConnection(connection: ConnectionNotice | null): void {\n patchState(store, { connection });\n },\n\n remove(id: string): void {\n patchState(store, state => {\n const uploads = state.uploads.filter(u => u.id !== id);\n // The job is forgotten once its last row is gone; while one is left, the job is still followed.\n const gone = state.uploads.filter(u => u.id === id && !uploads.some(other => jobOf(other) === jobOf(u)));\n return { uploads, ignoredJobs: withIgnored(state.ignoredJobs, gone) };\n });\n },\n\n /** Drops a finished workspace's files from the tracker; they stay counted as indexed. */\n removeWorkspace(workspaceId: string): void {\n patchState(store, state => ({\n uploads: state.uploads.filter(u => u.workspaceId !== workspaceId),\n workspaces: state.workspaces.filter(w => w.id !== workspaceId),\n clearedCount: state.clearedCount + state.uploads.filter(u => u.workspaceId === workspaceId).length,\n ignoredJobs: withIgnored(\n state.ignoredJobs,\n state.uploads.filter(u => u.workspaceId === workspaceId)\n )\n }));\n },\n\n reset(): void {\n patchState(store, state => ({ ...initialState, ignoredJobs: withIgnored(state.ignoredJobs, state.uploads) }));\n }\n }))\n);\n","import { Injectable, signal } from \"@angular/core\";\nimport { fetchPrincipal } from \"@sinequa/atomic\";\n\nexport interface CurrentUser {\n /** The principal's `userId` (`domain|id`) — the id space a workspace's owners/readers use. */\n id: string;\n name: string;\n}\n\n/**\n * The logged-in user's identity — the one thing @sinequa/atomic has no\n * synchronous notion of. Resolved once, on first injection, through\n * `fetchPrincipal()`; `user()` is `undefined` until it settles (and stays so\n * if the request fails).\n *\n * Callers render off `user()` straight away and treat \"unknown\" as a valid\n * fallback (a raw-id badge, the generic \"You\" label) rather than an error.\n * Those that must not act on \"unknown\" as if it were an answer — the\n * \"you can't remove yourself\" rule needs to know which row is yours —\n * await {@link whenReady}.\n */\n@Injectable({ providedIn: \"root\" })\nexport class CurrentUserService {\n private readonly _user = signal<CurrentUser | undefined>(undefined);\n private readonly ready: Promise<CurrentUser | undefined>;\n\n readonly user = this._user.asReadonly();\n\n constructor() {\n this.ready = Promise.resolve()\n .then(() => fetchPrincipal())\n // `userId`, not `id`: the container API rejects an identity that is not in\n // `domain|id` form, and Principal.id is the bare account id. No fallback to\n // `id` if `userId` is somehow empty: `id` is precisely the form the API\n // refuses, so falling back would guarantee the 400 instead of avoiding it.\n // Leaving the user unknown is the softer failure.\n .then(p => {\n const user = p?.userId ? { id: p.userId, name: p.fullName || p.name } : undefined;\n this._user.set(user);\n return user;\n })\n .catch(() => undefined);\n }\n\n /** The same identity as `user()`, but awaitable. Resolves `undefined` if the principal request failed. */\n whenReady(): Promise<CurrentUser | undefined> {\n return this.ready;\n }\n}\n","import { globalConfig } from \"@sinequa/atomic\";\n\n/**\n * The configured backend as an absolute base URL, or `undefined` when no\n * `backendUrl` is configured (same-origin host — atomic itself keeps its\n * endpoints relative in that case).\n */\nfunction backendBase(): string | undefined {\n const backendUrl = globalConfig.backendUrl;\n if (!backendUrl) return undefined;\n // Trailing slash so a relative endpoint resolves *under* backendUrl instead\n // of replacing its last path segment; any slash the host already wrote is\n // collapsed first. `backendUrl` may itself be relative ('/sinequa').\n return new URL(`${backendUrl.replace(/\\/+$/, \"\")}/`, window.location.href).href;\n}\n\n/**\n * Resolves a path that atomic does not fetch for us to an absolute URL: the\n * tus upload endpoint, and the `documentCachedContentUrl` the preview web\n * service returns for an `<iframe src>`. Both are URLs the browser loads\n * directly rather than through atomic's own `get()`/`post()`, so neither picks\n * up `backendUrl` on its own.\n *\n * - an absolute endpoint always wins (it is taken as-is);\n * - with a `backendUrl` configured, a root-relative endpoint\n * ('/api/v2/agent-workspace/ingest/file') resolves *under* it, so a backendUrl\n * carrying a path prefix ('https://host/sinequa') isn't bypassed;\n * - with no `backendUrl`, it resolves against the page.\n */\nexport function resolveEndpointUrl(endpoint: string): URL {\n const base = backendBase();\n if (!base) return new URL(endpoint, window.location.href);\n return new URL(endpoint.replace(/^\\/+/, \"\"), base);\n}\n","// Helpers over @sinequa/atomic's fetchUploadStatus() response, which groups\n// jobs by workspace.\nimport type { UploadJobStatus, UploadStatusResponse } from \"@sinequa/atomic\";\n\n/** True once a job can no longer change — nothing left to poll for. */\nexport function isJobTerminal(job: UploadJobStatus): boolean {\n if (job.uploadStatus === \"upload_interrupted\") return true;\n return job.indexingStatus === \"complete\" || job.indexingStatus === \"failed\" || job.indexingStatus === \"interrupted\";\n}\n\n/** Flattens the grouped response into a plain job list. */\nexport function allJobs(res: UploadStatusResponse): UploadJobStatus[] {\n return (res.workspaces ?? []).flatMap(c => c.jobs ?? []);\n}\n\n/** A job and the workspace the response grouped it under. */\nexport interface WorkspaceJob {\n job: UploadJobStatus;\n workspaceId: string;\n workspaceName: string;\n}\n\n/** Like {@link allJobs}, but keeps the workspace each job belongs to. */\nexport function workspaceJobs(res: UploadStatusResponse): WorkspaceJob[] {\n return (res.workspaces ?? []).flatMap(c => (c.jobs ?? []).map(job => ({ job, workspaceId: c.workspaceId, workspaceName: c.workspaceName })));\n}\n\n/** How long the panel stays on screen once every upload is over. */\nexport const FINISHED_UPLOAD_VISIBLE_MS = 2 * 60 * 1000;\n","/**\n * `crypto.randomUUID()` is only available in a secure context (HTTPS, or HTTP on\n * localhost). This falls back to a `getRandomValues()`-based UUID v4 so the\n * upload still works over plain HTTP on a hostname or an IP.\n */\nexport function generateUUID(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx\n\n const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","// Silent, non-navigating attempt to make the session usable again after a 401.\n//\n// Why this exists: an upload that outlives a connection drop comes back to a\n// server that has since forgotten it. tus keeps retrying (see\n// UPLOAD_RETRY_DELAYS in tus-upload.ts) but every attempt carries the\n// stale CSRF token it was given at creation time, so every attempt is a 401 —\n// the retry budget burns down without a single byte moving. The status poll\n// fails the same way at the same moment, for the same reason.\n//\n// Recovering means getting a fresh token into storage; atomic's createHeaders()\n// reads it per request, so tus's onBeforeRequest picks it up on the very next\n// attempt with nothing else to do.\nimport { tryAutoAuthentication } from \"@sinequa/atomic\";\n\nlet _inFlight: Promise<boolean> | null = null;\n\n/**\n * Re-establishes the session without leaving the page, and reports whether it\n * worked.\n *\n * Deliberately `tryAutoAuthentication()` and nothing else:\n *\n * - `hasSession()` is the wrong probe. It answers `true` immediately when a\n * token is in storage, without asking the server — and a stale token in\n * storage is precisely the situation being recovered from, so it would report\n * success while every request kept coming back 401.\n * - `login()` is the wrong door. In `oauth`/`saml` mode it navigates to the\n * identity provider, and a page load destroys every in-flight upload: tus\n * holds the `File` in memory and nothing persists it. Signing in again has to\n * stay the user's explicit choice, not something a background retry triggers.\n *\n * `tryAutoAuthentication()` hits a protected endpoint with `redirect: \"manual\"`,\n * so it never navigates, and a `200` carries the `sinequa-jwt-refresh` header\n * that atomic stores for us. It covers the cases worth covering automatically:\n * an ambient SSO session (IIS/proxy), an OIDC session still live at the IdP, and\n * a cookie that outlived its CSRF token. It answers `false` — never throws —\n * when the user is genuinely signed out, which is the caller's cue to stop\n * retrying and say so.\n *\n * Single-flight: several uploads and the status poll all hit 401 within the same\n * instant, and they share one probe rather than racing a dozen of them.\n *\n * @returns `true` when the session is usable again.\n */\nexport function recoverSession(): Promise<boolean> {\n _inFlight ??= tryAutoAuthentication()\n .catch(() => false)\n .finally(() => {\n _inFlight = null;\n });\n return _inFlight;\n}\n","// One cancellable polling loop, shared by every upload.\n//\n// The loop paces itself from what a tick reports:\n// - `pending: false` -> nothing left to watch, stop (start() revives it);\n// - `changed: true` -> something moved, keep the base interval;\n// - `changed: false` -> idle, widen the delay geometrically up to maxIntervalMs\n// so a job the server never resolves costs a request a minute, not one a second.\n// A thrown tick backs off the same way and does NOT stop the loop: a network\n// blip should not silently end the tracking.\n\nexport interface PollResult {\n /** Something is still being watched — keep polling. */\n pending: boolean;\n /** This tick observed a change; resets the idle backoff. */\n changed?: boolean;\n}\n\nexport interface StatusPollerOptions {\n /** Delay between ticks while things are moving (default 1500 ms). */\n intervalMs?: number;\n /** Ceiling the idle/failure backoff climbs to (default 30000 ms). */\n maxIntervalMs?: number;\n /** Runs one poll. `signal` aborts when the poller is stopped mid-flight. */\n tick(signal: AbortSignal): Promise<PollResult>;\n}\n\nexport class StatusPoller {\n private readonly _intervalMs: number;\n private readonly _maxIntervalMs: number;\n private readonly _tick: (signal: AbortSignal) => Promise<PollResult>;\n\n private _timer: ReturnType<typeof setTimeout> | null = null;\n private _abort: AbortController | null = null;\n private _running = false;\n private _idleSteps = 0;\n\n constructor(options: StatusPollerOptions) {\n this._intervalMs = options.intervalMs ?? 1500;\n this._maxIntervalMs = options.maxIntervalMs ?? 30000;\n this._tick = options.tick;\n }\n\n get running(): boolean {\n return this._running;\n }\n\n /** Idempotent: calling it while already polling does nothing. */\n start(): void {\n if (this._running) return;\n this._running = true;\n this._idleSteps = 0;\n this._schedule(0);\n }\n\n /** Idempotent, and leaves the poller restartable. */\n stop(): void {\n this._running = false;\n if (this._timer) {\n clearTimeout(this._timer);\n this._timer = null;\n }\n this._abort?.abort();\n this._abort = null;\n }\n\n private _schedule(delay: number): void {\n if (!this._running) return;\n this._timer = setTimeout(() => {\n this._timer = null;\n void this._run();\n }, delay);\n }\n\n private _backoffDelay(): number {\n return Math.min(this._intervalMs * 2 ** this._idleSteps, this._maxIntervalMs);\n }\n\n private async _run(): Promise<void> {\n if (!this._running) return;\n const controller = new AbortController();\n this._abort = controller;\n try {\n const result = await this._tick(controller.signal);\n if (!this._running) return;\n if (!result.pending) {\n this.stop();\n return;\n }\n if (result.changed) this._idleSteps = 0;\n else this._idleSteps++;\n this._schedule(result.changed ? this._intervalMs : this._backoffDelay());\n } catch {\n // An abort is a deliberate stop, not a failure — nothing to re-arm.\n if (controller.signal.aborted || !this._running) return;\n this._idleSteps++;\n this._schedule(this._backoffDelay());\n } finally {\n if (this._abort === controller) this._abort = null;\n }\n }\n}\n","import { InjectionToken } from \"@angular/core\";\nimport { createHeaders } from \"@sinequa/atomic\";\nimport { DetailedError, Upload } from \"tus-js-client\";\n\n/**\n * Retry schedule for a tus chunk, in ms. tus caps the number of attempts at this\n * array's LENGTH, so surviving an outage is a matter of how many entries there\n * are, not just how long they are. The total (~5 min) is deliberately sized on\n * the server's own retention: its cleanup reclaims an incomplete upload idle for\n * 5 minutes and then answers 410 Gone, so retrying past that cannot succeed — at\n * which point the upload is recovered by the `online` handler instead.\n */\nexport const UPLOAD_RETRY_DELAYS = [0, 1000, 3000, 5000, 10000, 15000, 20000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000];\n\n/**\n * Statuses the ingest endpoint uses to say \"this upload will never be accepted\":\n * retrying them only burns requests. Everything else — a network error (status\n * 0), a 5xx, a 429 — is transient and worth another attempt.\n *\n * 401 is deliberately NOT in here even though it is a 4xx. An upload that\n * outlives a connection drop comes back holding a CSRF token the server has\n * since forgotten, so every attempt 401s and the whole retry budget burns\n * without a byte moving — which is exactly the failure this list would make\n * permanent. It is handled on its own by the service: recover the session, then\n * let the same retry carry the fresh token.\n */\nexport const FATAL_UPLOAD_STATUSES: ReadonlySet<number> = new Set([400, 403, 404, 409, 410, 413, 422, 507]);\n\nconst CHUNK_SIZE = 5 * 1024 * 1024;\n\n/** What an upload tells whoever drives it. */\nexport interface UploadEvents {\n /**\n * Whether tus should try again after a failed attempt. `status` is the HTTP\n * status of the failure, 0 for a network error; `attempt` is 0-based and is\n * the index into {@link UPLOAD_RETRY_DELAYS} of the delay tus waited before it.\n */\n shouldRetry(status: number, attempt: number): boolean;\n /** Any response came back — `status` tells whether the connection is working again. */\n responded(status: number): void;\n /** Resolves once the request may go out: a session recovery in flight has to finish first, it is what the retry is for. */\n beforeRequest(): Promise<void>;\n progress(bytesUploaded: number, bytesTotal: number | null): void;\n /** The server holds every byte. Indexing is a separate server-side job with no tus equivalent. */\n success(): void;\n /** The retry budget is spent, or the server refused for good. */\n failed(error: Error): void;\n}\n\nexport interface UploadOptions {\n /** Absolute: tus must not resolve a relative endpoint against the host page instead of the configured backend. */\n endpoint: string;\n /** The ingest endpoint validates these keys and answers 400 if one is missing. */\n metadata: Record<string, string>;\n events: UploadEvents;\n}\n\n/** An upload in flight, whatever moves its bytes. */\nexport interface UploadHandle {\n /** Starts it — resuming a previous attempt at the same file when tus recognises it (same name, size, type, date). */\n begin(): void;\n /** Sends it again after a failure; tus re-reads the server's offset first, so only the missing bytes go out. */\n resume(): void;\n /**\n * Stops it. With `terminate`, the server is asked to drop what it received\n * (tus termination extension) — which it may not support, in which case this rejects.\n */\n abort(terminate: boolean): Promise<void>;\n}\n\nexport type UploadFactory = (file: File, options: UploadOptions) => UploadHandle;\n\n/** The HTTP status behind a tus error: 0 when there was no response (a network error). */\nexport function tusErrorStatus(error: DetailedError): number {\n return error.originalResponse ? error.originalResponse.getStatus() : 0;\n}\n\n/** Sends `file` with tus. */\nexport const createTusUpload: UploadFactory = (file, { endpoint, metadata, events }) => {\n const upload = new Upload(file, {\n endpoint,\n chunkSize: CHUNK_SIZE,\n uploadSize: file.size,\n retryDelays: UPLOAD_RETRY_DELAYS,\n removeFingerprintOnSuccess: true,\n metadata,\n // tus's default policy stops retrying the moment `navigator.onLine` reports\n // false — exactly when a dropped connection needs riding out, which is why a\n // short outage used to kill the transfer outright. Retry on anything\n // transient and let the schedule (or the `online` handler) do the waiting.\n onShouldRetry: (error, attempt) => events.shouldRetry(tusErrorStatus(error), attempt),\n onAfterResponse: (_request, response) => events.responded(response.getStatus()),\n // tus awaits this hook, so waiting here is what makes the first retry (delay\n // 0) useful instead of a guaranteed second 401. The headers are read per\n // attempt, so a refreshed token is picked up with nothing else to do.\n onBeforeRequest: async request => {\n await events.beforeRequest();\n createHeaders().forEach((value, key) => request.setHeader(key, value));\n },\n onProgress: (bytesUploaded, bytesTotal) => events.progress(bytesUploaded, bytesTotal),\n onSuccess: () => events.success(),\n onError: error => events.failed(error)\n });\n\n return {\n begin: () => {\n upload\n .findPreviousUploads()\n .catch(() => [])\n .then(previous => {\n if (previous.length) upload.resumeFromPreviousUpload(previous[0]);\n upload.start();\n });\n },\n resume: () => upload.start(),\n abort: terminate => upload.abort(terminate)\n };\n};\n\n/** Overridable so the service can be driven without a network. */\nexport const UPLOAD_FACTORY = new InjectionToken<UploadFactory>(\"UPLOAD_FACTORY\", {\n providedIn: \"root\",\n factory: () => createTusUpload\n});\n","import { DestroyRef, inject, Injectable } from \"@angular/core\";\nimport { ApiError, fetchUploadStatus, onAuthEvent } from \"@sinequa/atomic\";\nimport type { UploadStatusResponse } from \"@sinequa/atomic\";\nimport type { ConnectionNotice } from \"../models/upload.model\";\nimport { CurrentUserService } from \"../services/current-user.service\";\nimport { WorkspacesStore } from \"../store/workspaces.store\";\nimport { resolveEndpointUrl } from \"../utils/base-url\";\nimport { getDocumentType } from \"../utils/doc-type\";\nimport { allJobs, workspaceJobs, isJobTerminal } from \"../utils/upload-jobs\";\nimport { generateUUID } from \"../utils/uuid\";\nimport { recoverSession } from \"./session-recovery\";\nimport { PollResult, StatusPoller } from \"./status-poller\";\nimport { FATAL_UPLOAD_STATUSES, UPLOAD_FACTORY, UPLOAD_RETRY_DELAYS, UploadHandle } from \"./tus-upload\";\nimport { RemoteJobEntry, UploadTrackerStore } from \"./upload-tracker.store\";\n\n/** A retry tus has scheduled: which attempt it is (0-based), and when it fires. */\ninterface RetrySchedule {\n attempt: number;\n retryAtMs: number;\n}\n\n/** A file being sent, as the service and the tracker follow it. `id` is `<jobId>/<fileName>`, which never changes. */\ninterface UploadItem {\n id: string;\n workspaceId: string;\n}\n\n/** An upload the service can still act on. */\ninterface UploadEntry {\n handle: UploadHandle;\n workspaceId: string;\n /** The server refused this file for good: sending it again would only be refused again. */\n refused: boolean;\n}\n\nexport interface UploadTarget {\n id: string;\n name: string;\n}\n\nexport interface AddFilesOptions {\n /** The tus endpoint — the `upload-endpoint` of the host. Relative paths resolve against the configured backend. */\n endpoint: string;\n}\n\n/**\n * Sends files to a workspace and follows them until they are indexed.\n *\n * Two independent things happen after a file is handed over, and both can stall\n * on the same cause (a lost connection, a lapsed session):\n *\n * - the byte transfer, by tus, which retries on its own schedule;\n * - indexing, a server-side job tus knows nothing about, observed by polling\n * the Document Upload status API.\n *\n * One small state machine here owns both, and the tracker's connection banner\n * speaks for both. What the user sees lives in `UploadTrackerStore`; this\n * service only writes to it.\n *\n * It never writes a file into a workspace's list: the list is the index, and a file\n * enters it only once the container listing returns it. What the service does for the\n * list is re-read that listing while something is in progress.\n *\n * Root-scoped: an upload must outlive the view that started it. It registers its\n * window and auth listeners once, and removes them when the application is destroyed.\n */\n@Injectable({ providedIn: \"root\" })\nexport class UploadService {\n private readonly tracker = inject(UploadTrackerStore);\n private readonly workspaces = inject(WorkspacesStore);\n private readonly currentUser = inject(CurrentUserService);\n private readonly createUpload = inject(UPLOAD_FACTORY);\n\n private readonly uploads = new Map<string, UploadEntry>();\n /**\n * Documents this client is still transferring or waiting on. The server's\n * status store can lag a job by a tick or two, so this is what keeps the\n * polling loop alive until it catches up — and emptying it lets it stop.\n */\n private readonly inFlight = new Set<string>();\n private readonly poller = new StatusPoller({ tick: signal => this.pollUploadStatus(signal) });\n private focusedWorkspaceId: string | null = null;\n\n // ── connection loss & session recovery ──\n /** Document id -> the retry tus has scheduled for it. Empty means nothing is waiting. */\n private readonly retrying = new Map<string, RetrySchedule>();\n /** A recovery is in flight. Distinct from `signedOut`: one is \"asking\", the other is the answer. */\n private recovering = false;\n /** The last recovery said the user is genuinely signed out. Reset the moment anything re-authenticates. */\n private signedOut = false;\n /** When the trouble started, so the banner can say for how long. 0 while healthy. */\n private lostSinceMs = 0;\n private recovery: Promise<boolean> | null = null;\n\n constructor() {\n const resume = () => this.resumeStalled();\n window.addEventListener(\"online\", resume);\n\n // The other way back in: the user signed in again (here, or in another tab\n // sharing this session storage). `login()` is never called from here — it\n // navigates for oauth/saml, and a page load destroys every in-flight upload\n // — so this is how a real sign-in reaches us.\n const offAuth = onAuthEvent(\"authenticated\", ({ authenticated }) => {\n if (!authenticated) return;\n this.signedOut = false;\n this.resumeStalled();\n this.poller.start();\n this.publishConnection();\n });\n\n inject(DestroyRef).onDestroy(() => {\n window.removeEventListener(\"online\", resume);\n offAuth?.();\n this.poller.stop();\n });\n }\n\n // ── public API ──\n\n /**\n * Stages the files as one ingest job and starts sending them. Everything the\n * user picked goes out as ONE batch (one job id).\n *\n * No client-side gatekeeping: no type allow-list, no count or size cap. What\n * can be indexed is the server's call (the workspace's conversion plan); the\n * files it will not take come back in the job's `rejected[]`. The server does\n * cap a job at 1 GiB on the declared total and answers 413 past it.\n *\n * @returns The tracked id of each file, in order.\n */\n addFiles(workspace: UploadTarget, files: File[], options: AddFilesOptions): string[] {\n if (!files.length) return [];\n\n // The three job-wide values go out as tus metadata on every file of the job.\n const jobId = generateUUID();\n const fileCount = files.length;\n const totalBytes = files.reduce((total, f) => total + f.size, 0);\n\n // The id is the key the tracker and this service follow the file by. It is NOT the\n // server's: the listing names a document `/DocUpload/…|<containerId>/<fileName>`.\n const items: UploadItem[] = files.map(file => ({ id: `${jobId}/${file.name}`, workspaceId: workspace.id }));\n\n this.tracker.track(\n files.map((file, i) => ({ id: items[i].id, name: file.name, type: getDocumentType(file.name) })),\n workspace.id,\n workspace.name\n );\n items.forEach((item, i) => {\n this.inFlight.add(item.id);\n this.startUpload(item, files[i], { jobId, fileCount, totalBytes }, options.endpoint);\n });\n this.poller.start();\n\n return items.map(item => item.id);\n }\n\n /**\n * Abandons a transfer and forgets it: stops it, and drops the file from the\n * tracker. Works while it is sending\n * and once it has failed; once the bytes are in, indexing is the server's and\n * there is nothing left to cancel.\n *\n * The server is asked to discard what it received (tus termination extension).\n * It may not support that, in which case the transfer is only stopped and the\n * leftover is reclaimed by the server's own retention.\n */\n async cancel(id: string): Promise<void> {\n const entry = this.uploads.get(id);\n if (!entry) return;\n\n // Forget it first: nothing the stopping transfer may still report should touch the state again.\n this.uploads.delete(id);\n this.inFlight.delete(id);\n this.clearRetryNotice(id);\n this.tracker.remove(id);\n\n try {\n await entry.handle.abort(true);\n } catch {\n await entry.handle.abort(false).catch(() => undefined);\n }\n }\n\n /** Sends a failed transfer again, from where the server left off. Only a transfer the server did not refuse. */\n retry(id: string): void {\n const entry = this.uploads.get(id);\n const upload = this.tracker.uploads().find(u => u.id === id);\n if (!entry || entry.refused || upload?.status !== \"error\") return;\n\n this.tracker.restart(id);\n this.inFlight.add(id);\n entry.handle.resume();\n this.poller.start();\n }\n\n /**\n * Picks up every transfer whose retry budget ran out while the connection was\n * down. Safe to call at any time: a transfer still retrying is left alone.\n * The `File` is held by tus for as long as the tab is open, which is what makes\n * this recovery automatic — an upload does not survive the tab closing.\n */\n resumeStalled(): void {\n for (const id of this.uploads.keys()) this.retry(id);\n }\n\n /**\n * Tells the service which workspace is open, so the status polling can be\n * scoped to it (the API documents that as the cheaper lookup) and re-read its\n * listing. `null` when none is. Starts the polling: a page reload loses every\n * local tus handle and the listing no longer says what is in progress, so the\n * status API is the only way to know. The loop stops by itself after one tick\n * when nothing is.\n */\n watch(workspaceId: string | null): void {\n this.focusedWorkspaceId = workspaceId;\n if (workspaceId) this.poller.start();\n }\n\n startPolling(): void {\n this.poller.start();\n }\n\n stopPolling(): void {\n this.poller.stop();\n }\n\n // ── sending ──\n\n private startUpload(item: UploadItem, file: File, job: { jobId: string; fileCount: number; totalBytes: number }, endpoint: string): void {\n const workspaceId = item.workspaceId;\n if (!endpoint) {\n this.inFlight.delete(item.id);\n this.markError(item);\n return;\n }\n\n const entry: UploadEntry = {\n workspaceId,\n refused: false,\n handle: this.createUpload(file, {\n endpoint: resolveEndpointUrl(endpoint).href,\n // The ingest endpoint validates exactly these keys and answers 400 if one is\n // missing. `totalBytes` and `fileCount` describe the WHOLE job, not this\n // file — identical on every upload of the job — so the server knows how much\n // to expect before it is complete.\n metadata: { workspaceId, jobId: job.jobId, fileName: file.name, totalBytes: String(job.totalBytes), fileCount: String(job.fileCount) },\n events: {\n shouldRetry: (status, attempt) => {\n if (status === 401) {\n // Recoverable, not fatal — unless a recovery has already come back\n // \"genuinely signed out\", in which case another attempt is just another\n // 401. Stop there: the upload lands in the error state and is picked up\n // again once the user is back.\n void this.recoverSessionAndResume();\n if (this.signedOut) {\n this.clearRetryNotice(item.id);\n return false;\n }\n } else if (FATAL_UPLOAD_STATUSES.has(status)) {\n entry.refused = true;\n this.clearRetryNotice(item.id);\n return false;\n }\n // tus waits retryDelays[attempt] before this attempt and passes that same\n // index here, so the banner's countdown is the real delay, not an estimate.\n this.noteRetryScheduled(item.id, attempt);\n return true;\n },\n // Anything that came back cleanly means the connection works again for this\n // upload, whatever the retry bookkeeping still says.\n responded: status => {\n if (status < 400) this.noteHealthy(item.id);\n },\n // A recovery kicked off by a previous 401 may still be in flight, and\n // carrying the token it is fetching is the entire point of this retry.\n beforeRequest: async () => {\n if (this.recovery) await this.recovery;\n },\n progress: (bytesUploaded, bytesTotal) => {\n // The browser reports bytes handed to the socket, not bytes the server\n // processed: on a fast transfer a stale event can still arrive after\n // success moved the file to indexing. Drop anything that comes once the\n // upload phase is over, so it cannot stomp the indexing percentage.\n if (this.tracker.uploads().find(u => u.id === item.id)?.status !== \"uploading\") return;\n this.tracker.setProgress(item.id, bytesTotal ? (bytesUploaded / bytesTotal) * 100 : 0);\n },\n success: () => {\n // Bytes are in; indexing is now the server's business, so the poller owns\n // this document from here. Nothing left to resume.\n this.noteHealthy(item.id);\n this.uploads.delete(item.id);\n this.markIndexing(item);\n this.poller.start();\n },\n failed: () => {\n // The handle is kept: the retry budget is spent, but the upload is not lost —\n // `retry()` can pick it up again and tus resumes from the server's offset.\n this.clearRetryNotice(item.id);\n this.inFlight.delete(item.id);\n this.markError(item, { retryable: !entry.refused });\n }\n }\n })\n };\n\n this.uploads.set(item.id, entry);\n entry.handle.begin();\n }\n\n // ── what a file's state change writes ──\n\n private markIndexing(item: UploadItem): void {\n this.tracker.setIndexing(item.id);\n }\n\n private markError(item: UploadItem, options: { retryable?: boolean } = {}): void {\n this.tracker.setError(item.id, options);\n }\n\n // ── following indexing ──\n //\n // - GET .../agent-workspace/status -> the tracker: aggregate, errors[] and rejected[] PER JOB\n // - GET .../container/{id}/documents -> the workspace's list, which is the index and nothing else\n\n private async pollUploadStatus(signal: AbortSignal): Promise<PollResult> {\n try {\n const result = await this.runPollTick(signal);\n this.noteHealthy();\n return result;\n } catch (error) {\n // A 401 means the session lapsed, and every further tick would be another 401\n // at the poller's own pace. Park the loop on the recovery instead: if it works\n // the loop carries on (pending: true) and the next tick does the real work; if\n // not, report nothing pending so the poller stops, and leave restarting to the\n // `authenticated` listener or the banner's Retry button.\n if (error instanceof ApiError && error.status === 401) {\n return { pending: await this.recoverSessionAndResume(), changed: false };\n }\n throw error;\n }\n }\n\n private async runPollTick(signal: AbortSignal): Promise<PollResult> {\n const workspaceId = this.focusedWorkspaceId;\n\n // Scoped to the open workspace when there is one, and unscoped otherwise, so\n // the tracker still follows a batch after the user navigates away from it.\n const response = await fetchUploadStatus(workspaceId ? { workspaceId } : {}, { signal });\n const jobs = allJobs(response);\n\n // Jobs started elsewhere come first, so that `applyJobs` finds their rows to update.\n const remote = await this.remoteJobs(response, signal);\n let changed = this.tracker.applyRemoteJobs(remote);\n changed = this.tracker.applyJobs(jobs) || changed;\n this.settleFinishedUploads();\n const pending = jobs.some(job => !isJobTerminal(job));\n\n // The list follows the index, not the status: a file shows up once the listing returns it.\n // The last tick of a loop reads it too, which is when a file that just got indexed comes in.\n if (workspaceId) changed = (await this.refreshListing(workspaceId, signal)) || changed;\n\n return { pending: pending || this.inFlight.size > 0, changed };\n }\n\n /**\n * The jobs of the response the panel should show although this client did not send\n * them. The server decides which ones the response holds — those in progress or just\n * ended, on the workspaces the user can write to — so none is filtered by role or date\n * here; only, with a workspace open, those that belong to it are kept. Whether the user\n * started one is read from `initiatedBy`, which is their `userId`, so it holds after a\n * page reload too.\n */\n private async remoteJobs(response: UploadStatusResponse, signal: AbortSignal): Promise<RemoteJobEntry[]> {\n const me = (await this.currentUser.whenReady())?.id;\n if (signal.aborted) return [];\n\n const entries: RemoteJobEntry[] = [];\n for (const { job, workspaceId, workspaceName: apiName } of workspaceJobs(response)) {\n if (this.focusedWorkspaceId && this.focusedWorkspaceId !== workspaceId) continue;\n const workspaceName = this.workspaces.workspace(workspaceId)?.name ?? apiName;\n entries.push({ job, workspaceId, workspaceName, mine: !!me && job.initiatedBy === me });\n }\n return entries;\n }\n\n /** Re-reads the workspace's listing. @returns Whether it differs from what was shown. */\n private async refreshListing(workspaceId: string, signal: AbortSignal): Promise<boolean> {\n const before = JSON.stringify(this.workspaces.documentsOf(workspaceId));\n await this.workspaces.loadDocuments(workspaceId, signal);\n return JSON.stringify(this.workspaces.documentsOf(workspaceId)) !== before;\n }\n\n /** The job-level status can finish a file this client was still holding the poller for: release it. */\n private settleFinishedUploads(): void {\n for (const upload of this.tracker.uploads()) {\n if (upload.status === \"done\") this.inFlight.delete(upload.id);\n }\n }\n\n // ── connection loss & session recovery ──\n //\n // The tus transfers and the status poll stall on the same cause, so one state\n // machine owns both and one banner speaks for both: what is waiting, for how\n // long, and whether the wait is worth anything.\n\n /** Transfers that spent their whole budget and are parked in the error state: what \"N uploads paused\" counts once retrying has stopped. */\n private stalledUploadCount(): number {\n return this.tracker.uploads().filter(u => u.status === \"error\" && u.retryable).length;\n }\n\n private publishConnection(): void {\n if (!this.retrying.size && !this.recovering && !this.signedOut) {\n this.lostSinceMs = 0;\n this.tracker.setConnection(null);\n return;\n }\n if (!this.lostSinceMs) this.lostSinceMs = Date.now();\n\n // The soonest retry is the one the countdown is about: it is the next thing that will actually happen.\n const soonest = [...this.retrying.values()].reduce<RetrySchedule | null>((best, r) => (best === null || r.retryAtMs < best.retryAtMs ? r : best), null);\n\n const base = {\n uploads: this.retrying.size || this.stalledUploadCount(),\n maxAttempts: UPLOAD_RETRY_DELAYS.length,\n sinceMs: this.lostSinceMs\n };\n\n let notice: ConnectionNotice;\n if (this.recovering) {\n notice = { ...base, phase: \"reconnecting\", attempt: soonest ? soonest.attempt + 1 : 0 };\n } else if (this.signedOut || !soonest) {\n notice = {\n ...base,\n phase: \"signed-out\",\n attempt: soonest ? soonest.attempt + 1 : 0,\n retry: () => {\n void this.recoverSessionAndResume();\n }\n };\n } else {\n notice = {\n ...base,\n phase: \"retrying\",\n // retryAttempt is 0-based; humans count attempts from 1.\n attempt: soonest.attempt + 1,\n retryAtMs: soonest.retryAtMs,\n // What is left of the budget after this attempt, measured from it: when tus will stop trying on its own.\n budgetEndsAtMs: soonest.retryAtMs + UPLOAD_RETRY_DELAYS.slice(soonest.attempt + 1).reduce((a, b) => a + b, 0)\n };\n }\n this.tracker.setConnection(notice);\n }\n\n private noteRetryScheduled(id: string, attempt: number): void {\n this.retrying.set(id, { attempt, retryAtMs: Date.now() + (UPLOAD_RETRY_DELAYS[attempt] ?? 0) });\n this.publishConnection();\n }\n\n /** Drops a scheduled retry — the attempt happened, or will never happen. */\n private clearRetryNotice(id: string): void {\n if (this.retrying.delete(id)) this.publishConnection();\n }\n\n /**\n * Something came back cleanly, so the session demonstrably works — whatever the\n * last recovery concluded. Without this, one `signed-out` verdict would outlive\n * the sign-in that fixed it and make the NEXT 401, on a healthy session, refuse\n * to retry.\n */\n private noteHealthy(id?: string): void {\n const hadRetry = id !== undefined && this.retrying.delete(id);\n const wasSignedOut = this.signedOut;\n this.signedOut = false;\n if (hadRetry || wasSignedOut) this.publishConnection();\n }\n\n /**\n * Recovers the session and picks up whatever was waiting on it. Single-flight:\n * every stalled upload and the status poll hit 401 within the same instant, and\n * one probe answers for all of them.\n */\n private recoverSessionAndResume(): Promise<boolean> {\n this.recovery ??= this.runRecovery().finally(() => {\n this.recovery = null;\n });\n return this.recovery;\n }\n\n private async runRecovery(): Promise<boolean> {\n this.recovering = true;\n this.publishConnection();\n const ok = await recoverSession();\n this.recovering = false;\n this.signedOut = !ok;\n if (ok) {\n // Uploads still inside their retry budget need nothing: the headers are read per\n // attempt, so the refreshed token is already theirs. Only the ones that ran out\n // have to be pushed.\n this.resumeStalled();\n this.poller.start();\n }\n this.publishConnection();\n return ok;\n }\n}\n","import { inject, Injectable, signal } from \"@angular/core\";\nimport { fetchPrincipalsByIds, searchPrincipals } from \"@sinequa/atomic\";\nimport type { SecurityPrincipal } from \"@sinequa/atomic\";\nimport { CurrentUserService } from \"./current-user.service\";\n\nexport interface PrincipalSummary {\n name: string;\n isGroup: boolean;\n /** Users only, and only when the directory has one — the access list's secondary line. */\n email?: string;\n}\n\n/** A principal id turned into something renderable. */\nexport interface ResolvedPrincipal {\n /** Display label — the cached name, or the raw id when it isn't cached. Never decorated: see `isSelf`. */\n name: string;\n /** Secondary line under the name, when the directory gave one. */\n email?: string;\n isGroup: boolean;\n /** False when the id has no cached name and `name` is the raw id. */\n resolved: boolean;\n /** True when this id is the logged-in user's. */\n isSelf: boolean;\n}\n\nconst STORAGE_KEY = \"document-workspaces:principal-name-cache\";\n\n/**\n * The label to show for a principal: a user's full name where it has one,\n * otherwise the account name. Groups only ever have `name`.\n */\nexport function principalDisplayName(principal: SecurityPrincipal): string {\n return principal.isUser ? principal.fullName || principal.name : principal.name;\n}\n\nfunction readStorage(): Record<string, PrincipalSummary> {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n return raw ? JSON.parse(raw) : {};\n } catch {\n return {};\n }\n}\n\nfunction writeStorage(map: Record<string, PrincipalSummary>): void {\n try {\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(map));\n } catch {\n // sessionStorage unavailable (private browsing, quota) — the cache just\n // won't persist across a reload; not worth surfacing an error for.\n }\n}\n\n/**\n * Names for the principal ids a workspace's owners/readers hold, which are\n * opaque `domain|id` strings the API never resolves for us.\n *\n * Persisted in `sessionStorage` per the product decision — it survives\n * navigation within the tab and is cleared when the tab closes — so an access\n * list doesn't flash raw ids on every open.\n *\n * `resolve()` is synchronous and reads signals, so a template or `computed`\n * that calls it re-renders by itself once {@link ensureResolved} fills a gap.\n * An id that is not cached shows up as the raw id, which is also what an id\n * the server can't resolve at all (a deleted account) keeps showing — still\n * enough to keep it in the owners/readers arrays on the next save rather than\n * silently revoking it.\n */\n@Injectable({ providedIn: \"root\" })\nexport class PrincipalDirectory {\n private readonly currentUser = inject(CurrentUserService);\n private readonly cache = signal<Record<string, PrincipalSummary>>(readStorage());\n\n /**\n * Resolves a principal id for display, identically wherever an owners/readers\n * id is rendered — the access list and the add/edit dialog's \"Share with\" chips.\n *\n * \"(you)\" is deliberately NOT folded into the name: it would end up in the\n * avatar's initials and in every message that interpolates a name. Views\n * render it as its own element off `isSelf`.\n */\n resolve(id: string): ResolvedPrincipal {\n const user = this.currentUser.user();\n const isSelf = !!user && user.id === id;\n const cached: PrincipalSummary | undefined = isSelf ? { name: user.name, isGroup: false } : this.cache()[id];\n return {\n name: cached ? cached.name : id,\n email: cached?.email,\n isGroup: cached ? cached.isGroup : false,\n resolved: !!cached,\n isSelf\n };\n }\n\n /** Records a name learned elsewhere (e.g. a person just picked in the principal picker). */\n remember(id: string, summary: PrincipalSummary): void {\n this.cache.update(map => ({ ...map, [id]: summary }));\n writeStorage(this.cache());\n }\n\n /**\n * Searches the directory by free text and remembers every name it learns, so a\n * person just picked already has one when the access list shows them.\n *\n * @param query - What the user typed.\n * @param signal - Cancels the request when a newer keystroke supersedes it.\n * @returns Users and groups alike; the caller decides which it can use.\n */\n async search(query: string, signal?: AbortSignal): Promise<SecurityPrincipal[]> {\n const { principals = [] } = await searchPrincipals({ offset: 0, limit: 20, search: query }, signal);\n this.cache.update(map => {\n const next = { ...map };\n for (const p of principals) {\n next[p.userId] = { name: principalDisplayName(p), isGroup: p.isGroup, email: p.isUser ? p.email : undefined };\n }\n return next;\n });\n writeStorage(this.cache());\n return principals;\n }\n\n /**\n * Fetches and caches the names for any of `ids` not already known.\n *\n * @param ids - Principal identities, typically a workspace's owners/readers.\n *\n * @returns Resolves once the cache has been updated. A failed lookup resolves\n * rather than rejecting: the UI already renders unresolved ids, and a name\n * is not worth failing an access dialog over.\n */\n async ensureResolved(ids: string[]): Promise<void> {\n const known = this.cache();\n const missing = [...new Set(ids)].filter(id => id && !known[id]);\n if (!missing.length) return;\n try {\n const principals = await fetchPrincipalsByIds(missing);\n this.cache.update(map => {\n const next = { ...map };\n for (const p of principals) {\n next[p.userId] = { name: principalDisplayName(p), isGroup: p.isGroup, email: p.isUser ? p.email : undefined };\n }\n return next;\n });\n writeStorage(this.cache());\n } catch {\n /* leaves those ids showing as raw ids — see the class comment */\n }\n }\n}\n","import { booleanAttribute, Directive, input, output, signal } from \"@angular/core\";\n\n/**\n * Turns an element into a place to drop files on, or to click to pick some.\n *\n * ```html\n * <div dropzone (filesSelected)=\"stage($event)\">Drop your files here</div>\n * ```\n *\n * Both ways end in the same `filesSelected` output. While files are dragged over\n * it the host gets a `data-dragover` attribute to style. It is a button for\n * assistive technology and the keyboard (Enter or Space opens the file picker).\n *\n * It does no gatekeeping — no type, count or size check: whatever is dropped\n * or picked is emitted, and what can be handled is the server's call.\n */\n@Directive({\n selector: \"[dropzone]\",\n host: {\n role: \"button\",\n \"[attr.tabindex]\": \"disabled() ? -1 : 0\",\n \"[attr.aria-disabled]\": \"disabled() ? 'true' : null\",\n \"[attr.data-dragover]\": \"dragging() ? '' : null\",\n \"(click)\": \"browse()\",\n \"(keydown.enter)\": \"onKey($event)\",\n \"(keydown.space)\": \"onKey($event)\",\n \"(dragenter)\": \"onDragEnter($event)\",\n \"(dragover)\": \"onDragOver($event)\",\n \"(dragleave)\": \"onDragLeave($event)\",\n \"(drop)\": \"onDrop($event)\"\n }\n})\nexport class Dropzone {\n /** Files dropped or picked, in the order the browser gave them. */\n readonly filesSelected = output<File[]>();\n readonly disabled = input(false, { alias: \"dropzoneDisabled\", transform: booleanAttribute });\n\n protected readonly dragging = signal(false);\n\n // dragenter / dragleave also fire when the cursor crosses a child element, so a\n // bare boolean would flicker: count the elements entered and left instead.\n private depth = 0;\n\n protected browse(): void {\n if (this.disabled()) return;\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.multiple = true;\n input.addEventListener(\"change\", () => this.emit(input.files));\n input.click();\n }\n\n protected onKey(event: Event): void {\n event.preventDefault(); // Space would scroll the page\n this.browse();\n }\n\n protected onDragEnter(event: DragEvent): void {\n if (!this.carriesFiles(event)) return;\n event.preventDefault();\n this.depth++;\n this.dragging.set(true);\n }\n\n protected onDragOver(event: DragEvent): void {\n if (!this.carriesFiles(event)) return;\n event.preventDefault(); // without it the browser refuses the drop\n if (event.dataTransfer) event.dataTransfer.dropEffect = this.disabled() ? \"none\" : \"copy\";\n }\n\n protected onDragLeave(event: DragEvent): void {\n if (!this.carriesFiles(event)) return;\n this.depth = Math.max(0, this.depth - 1);\n if (this.depth === 0) this.dragging.set(false);\n }\n\n protected onDrop(event: DragEvent): void {\n if (!this.carriesFiles(event)) return;\n event.preventDefault();\n this.depth = 0;\n this.dragging.set(false);\n if (!this.disabled()) this.emit(event.dataTransfer?.files ?? null);\n }\n\n /** A text or a link being dragged around the page is none of our business. */\n private carriesFiles(event: DragEvent): boolean {\n return !!event.dataTransfer?.types?.includes(\"Files\");\n }\n\n private emit(files: FileList | null): void {\n if (files?.length) this.filesSelected.emit(Array.from(files));\n }\n}\n","import { computed, Directive, input, model, signal } from \"@angular/core\";\n\n/**\n * Turns an element into the drag handle of a resizable panel.\n *\n * The panel's width is two-way bound through the directive's own name:\n *\n * ```html\n * <aside [style.width.px]=\"width()\">\n * <div [(resizeHandle)]=\"width\" resizeMin=\"280\" [resizeMax]=\"maxWidth()\"></div>\n * </aside>\n * ```\n *\n * It follows the WAI-ARIA separator pattern: the handle is focusable and the\n * arrow keys resize it, so the panel stays usable without a pointer. The pointer\n * is captured on `pointerdown`, so a drag keeps going when the cursor leaves the\n * handle or crosses an iframe (which would otherwise swallow the events).\n */\n@Directive({\n selector: \"[resizeHandle]\",\n exportAs: \"resizeHandle\",\n host: {\n role: \"separator\",\n \"aria-orientation\": \"vertical\",\n tabindex: \"0\",\n \"[attr.aria-valuenow]\": \"width()\",\n \"[attr.aria-valuemin]\": \"resizeMin()\",\n \"[attr.aria-valuemax]\": \"resizeMax() ?? null\",\n \"[attr.data-resizing]\": \"resizing() ? '' : null\",\n \"(pointerdown)\": \"onPointerDown($event)\",\n \"(pointermove)\": \"onPointerMove($event)\",\n \"(pointerup)\": \"onPointerEnd($event)\",\n \"(pointercancel)\": \"onPointerEnd($event)\",\n \"(keydown)\": \"onKeydown($event)\"\n }\n})\nexport class ResizeHandle {\n /** The panel's width in px. */\n readonly width = model.required<number>({ alias: \"resizeHandle\" });\n readonly resizeMin = input(280, { transform: Number });\n /** Upper bound in px; unbounded when absent. */\n readonly resizeMax = input<number | undefined>(undefined);\n /** px moved per arrow key press. */\n readonly resizeStep = input(16, { transform: Number });\n /**\n * Which edge of the panel the handle sits on — and therefore which way a drag\n * widens it: dragging a `left` handle to the left widens the panel.\n */\n readonly resizeEdge = input<\"left\" | \"right\">(\"left\");\n\n /** Whether the handle is being dragged. */\n readonly resizing = signal(false);\n private readonly direction = computed(() => (this.resizeEdge() === \"left\" ? -1 : 1));\n\n private startX = 0;\n private startWidth = 0;\n\n protected onPointerDown(event: PointerEvent): void {\n if (event.button !== 0) return;\n event.preventDefault(); // no text selection while dragging\n (event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);\n this.startX = event.clientX;\n this.startWidth = this.width();\n this.resizing.set(true);\n }\n\n protected onPointerMove(event: PointerEvent): void {\n if (!this.resizing()) return;\n this.width.set(this.clamp(this.startWidth + this.direction() * (event.clientX - this.startX)));\n }\n\n protected onPointerEnd(event: PointerEvent): void {\n if (!this.resizing()) return;\n this.resizing.set(false);\n (event.currentTarget as HTMLElement).releasePointerCapture?.(event.pointerId);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const towardsRight = event.key === \"ArrowRight\" ? 1 : -1;\n this.width.set(this.clamp(this.width() + this.direction() * towardsRight * this.resizeStep()));\n }\n\n private clamp(value: number): number {\n return Math.min(Math.max(value, this.resizeMin()), this.resizeMax() ?? Number.POSITIVE_INFINITY);\n }\n}\n","import type { WorkspaceRole } from \"../models/workspace.model\";\n\n/**\n * Whether the role may change a workspace (edit, share, delete, upload).\n * An unknown role cannot: hiding an action for a moment is the softer failure\n * than offering a reader one they will be refused.\n */\nexport function canWrite(role: WorkspaceRole | null | undefined): boolean {\n return role === \"creator\" || role === \"owner\";\n}\n","import { computed, Directive, input } from \"@angular/core\";\nimport type { WorkspaceRole } from \"../models/workspace.model\";\nimport { canWrite } from \"../utils/workspace-role\";\n\n/**\n * Hides an element from anyone who cannot change the workspace — a reader, or\n * whoever the role is not known for yet. One rule for every edit / share /\n * delete / upload control, instead of the check being repeated on each.\n *\n * ```html\n * <button [writerOnly]=\"workspace().currentUserRole\" (click)=\"edit()\">Edit</button>\n * ```\n *\n * It sets `hidden`, which also drops the element from the accessibility tree\n * and the tab order; a layout class that sets `display` must not override it.\n */\n@Directive({\n selector: \"[writerOnly]\",\n host: { \"[hidden]\": \"!writable()\" }\n})\nexport class WriterOnly {\n readonly writerOnly = input.required<WorkspaceRole | null | undefined>();\n\n protected readonly writable = computed(() => canWrite(this.writerOnly()));\n}\n","/** A slice of a text, flagged when it is what a search matched. */\nexport interface TextChunk {\n text: string;\n match: boolean;\n}\n\n/** Lower-cases and strips accents, so \"Resume\" finds \"Résumé\". */\nexport function fold(text: string): string {\n return text.normalize(\"NFD\").replace(/\\p{M}/gu, \"\").toLowerCase();\n}\n\n/**\n * Folds `text` while remembering, for each folded code unit, which slice of the\n * original it came from — folding can change the length (a combining mark\n * vanishes, `İ` becomes two units), so a match position in the folded text is\n * not a position in the original.\n */\nexport function foldWithOffsets(text: string): { folded: string; starts: number[]; ends: number[] } {\n let folded = \"\";\n const starts: number[] = [];\n const ends: number[] = [];\n let index = 0;\n for (const char of text) {\n const start = index;\n index += char.length;\n const piece = fold(char);\n for (let i = 0; i < piece.length; i++) {\n starts.push(start);\n ends.push(index);\n }\n folded += piece;\n }\n return { folded, starts, ends };\n}\n\n/**\n * Whether `text` contains `query`, ignoring case and accents. An empty query\n * matches everything.\n */\nexport function matchesText(text: string, query: string): boolean {\n if (!query) return true;\n const folded = fold(query);\n return !folded || fold(text).includes(folded);\n}\n\n/**\n * Splits `text` around every occurrence of `query` (case- and accent-insensitive),\n * keeping the original characters — the accents a user typed or stored are not\n * lost to the matching.\n *\n * It returns data rather than markup on purpose: the text is user-supplied\n * (a workspace or document name) and the view renders the chunks through Angular's\n * own escaping instead of interpolating them into HTML.\n */\nexport function splitByMatch(text: string, query: string): TextChunk[] {\n if (!text) return [];\n const foldedQuery = query ? fold(query) : \"\";\n if (!foldedQuery) return [{ text, match: false }];\n\n const { folded, starts, ends } = foldWithOffsets(text);\n const chunks: TextChunk[] = [];\n let cursor = 0;\n let from = 0;\n\n for (let hit = folded.indexOf(foldedQuery, from); hit !== -1; hit = folded.indexOf(foldedQuery, from)) {\n const start = starts[hit];\n const end = ends[hit + foldedQuery.length - 1];\n if (start > cursor) chunks.push({ text: text.slice(cursor, start), match: false });\n chunks.push({ text: text.slice(start, end), match: true });\n cursor = end;\n from = hit + foldedQuery.length;\n }\n\n if (!chunks.length) return [{ text, match: false }];\n if (cursor < text.length) chunks.push({ text: text.slice(cursor), match: false });\n return chunks;\n}\n","import { Pipe, PipeTransform } from \"@angular/core\";\nimport { splitByMatch, TextChunk } from \"../utils/text-match\";\n\n/**\n * Splits a text around what a search matched, ignoring case and accents.\n *\n * Unlike `highlightWord`, it keeps the original characters (that pipe returns\n * chunks of the accent-stripped text, which would display \"Donnees\" for\n * \"Données\") and is meant to be rendered, never bound to `innerHTML`:\n *\n * ```html\n * @for (chunk of workspace.name | highlightMatch: query(); track $index) {\n * @if (chunk.match) { <mark>{{ chunk.text }}</mark> } @else { {{ chunk.text }} }\n * }\n * ```\n */\n@Pipe({ name: \"highlightMatch\" })\nexport class HighlightMatchPipe implements PipeTransform {\n transform(text: string | null | undefined, query: string | null | undefined): TextChunk[] {\n return splitByMatch(text ?? \"\", query ?? \"\");\n }\n}\n","import { Temporal } from \"temporal-polyfill\";\nimport type { Message } from \"./message\";\n\n/**\n * \"Added 3 hours ago\" for a document's creation date, as a {@link Message} the\n * template translates.\n *\n * The relative part comes from `Intl.RelativeTimeFormat` rather than a catalog\n * entry per unit: one call localizes the number, the unit and the \"ago\" pattern,\n * and `numeric: \"auto\"` gives idiomatic forms (\"yesterday\", \"last week\") that a\n * per-unit catalog would have to spell out for every language.\n *\n * @param iso - ISO 8601 timestamp (`createdAt`).\n * @param lang - The active language.\n * @param now - Reference instant, injectable for tests.\n * @returns `null` when `iso` is missing or unparseable.\n */\nexport function relativeDateMessage(iso: string | undefined, lang: string, now: Temporal.Instant = Temporal.Now.instant()): Message | null {\n if (!iso) return null;\n let created: Temporal.Instant;\n try {\n created = Temporal.Instant.from(iso);\n } catch {\n return null;\n }\n\n // A date slightly in the future (clock skew between server and browser) reads as \"just now\".\n const elapsed = Temporal.Instant.compare(created, now) > 0 ? Temporal.Duration.from({ seconds: 0 }) : created.until(now, { largestUnit: \"hour\" });\n const minutes = Math.floor(elapsed.total(\"minute\"));\n // \"now\" reads oddly next to \"Added\", so the sub-minute case has its own entry.\n if (minutes < 1) return { key: \"documentWorkspaces.format.addedJustNow\" };\n\n const formatter = new Intl.RelativeTimeFormat(lang, { numeric: \"auto\" });\n const hours = Math.floor(elapsed.total(\"hour\"));\n const days = Math.floor(hours / 24);\n let relative: string;\n if (minutes < 60) relative = formatter.format(-minutes, \"minute\");\n else if (hours < 24) relative = formatter.format(-hours, \"hour\");\n else if (days < 7) relative = formatter.format(-days, \"day\");\n else if (days < 30) relative = formatter.format(-Math.floor(days / 7), \"week\");\n else relative = formatter.format(-Math.floor(days / 30), \"month\");\n\n return { key: \"documentWorkspaces.format.added\", params: { relative } };\n}\n\n/** `1.2 MB`, `340 KB`, `12 byte` — in the active language. Bytes, KB (rounded, at least 1), then MB. */\nexport function formatFileSize(bytes: number, lang: string): string {\n if (bytes < 1024) return new Intl.NumberFormat(lang, { style: \"unit\", unit: \"byte\" }).format(bytes);\n if (bytes < 1024 * 1024) {\n return new Intl.NumberFormat(lang, { style: \"unit\", unit: \"kilobyte\" }).format(Math.max(1, Math.round(bytes / 1024)));\n }\n return new Intl.NumberFormat(lang, { style: \"unit\", unit: \"megabyte\", maximumFractionDigits: 1 }).format(bytes / (1024 * 1024));\n}\n\n/** A date for a label — \"Sep 30, 2026\" — in the active language, or `null` when there is none or it cannot be read. */\nexport function formatDate(iso: string | undefined, lang: string): string | null {\n if (!iso) return null;\n try {\n return Temporal.Instant.from(iso).toLocaleString(lang, { dateStyle: \"medium\" });\n } catch {\n return null;\n }\n}\n","import { assertInInjectionContext, computed, inject, Signal } from \"@angular/core\";\nimport { toSignal } from \"@angular/core/rxjs-interop\";\nimport { TranslocoService } from \"@jsverse/transloco\";\nimport type { WorkspaceDocument } from \"../models/workspace.model\";\nimport { formatFileSize, relativeDateMessage } from \"../utils/format\";\nimport type { Message } from \"../utils/message\";\n\nexport interface DocumentLabels {\n /** \"1.2 MB\", or `null` when the size is not known — the API does not serve one, only a file uploaded in this session has it. */\n size: Signal<string | null>;\n /** \"Added 3 hours ago\", or `null` when the creation date is not known. */\n added: Signal<Message | null>;\n}\n\n/**\n * The size and date labels a document card and a document row both show,\n * following the active language.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectDocumentLabels(document: Signal<WorkspaceDocument>): DocumentLabels {\n assertInInjectionContext(injectDocumentLabels);\n const transloco = inject(TranslocoService);\n const lang = toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });\n\n return {\n size: computed(() => {\n const { size } = document();\n return size === undefined ? null : formatFileSize(size, lang());\n }),\n added: computed(() => relativeDateMessage(document().createdAt, lang()))\n };\n}\n","import { assertInInjectionContext, DestroyRef, inject, Signal, signal } from \"@angular/core\";\n\n/**\n * Whether the browser reports a network connection, kept up to date from the\n * window's `online` / `offline` events. `true` where there is no `navigator`\n * (server-side rendering).\n *\n * `navigator.onLine` only knows about the network interface, not whether the\n * server is reachable: treat `false` as certain and `true` as likely.\n *\n * Must be called from an injection context; the listeners are removed when it is destroyed.\n */\nexport function injectOnlineStatus(): Signal<boolean> {\n assertInInjectionContext(injectOnlineStatus);\n\n const online = signal(typeof navigator === \"undefined\" ? true : navigator.onLine);\n\n if (typeof window !== \"undefined\") {\n const goOnline = () => online.set(true);\n const goOffline = () => online.set(false);\n window.addEventListener(\"online\", goOnline);\n window.addEventListener(\"offline\", goOffline);\n inject(DestroyRef).onDestroy(() => {\n window.removeEventListener(\"online\", goOnline);\n window.removeEventListener(\"offline\", goOffline);\n });\n }\n\n return online.asReadonly();\n}\n","import { assertInInjectionContext, computed, Signal, signal } from \"@angular/core\";\n\nexport interface SelectionOptions<T> {\n /** What can currently be selected — pass the list as it is displayed (filtered), not the full one. */\n items: Signal<readonly T[]>;\n /** A stable id per item. */\n key: (item: T) => string;\n /**\n * Whether an item may be ticked at all — a workspace shared read-only cannot be\n * deleted, so it should not count towards \"3 selected\" nor be ticked by \"select all\".\n * Every item is selectable when absent. Read reactively: it may depend on signals.\n */\n selectable?: (item: T) => boolean;\n}\n\nexport interface Selection<T> {\n /** Whether the selection mode (checkboxes, selection bar) is on. */\n readonly active: Signal<boolean>;\n /** The displayed items that can be ticked — empty when there is nothing to offer the selection mode for. */\n readonly selectableItems: Signal<T[]>;\n /** The displayed, selectable items that are ticked. An item filtered out of `items` is not counted. */\n readonly selectedItems: Signal<T[]>;\n readonly count: Signal<number>;\n /** Every selectable displayed item is ticked (never true when there is none). */\n readonly allSelected: Signal<boolean>;\n /** Some, but not all, are ticked — the \"indeterminate\" state of a select-all checkbox. */\n readonly partiallySelected: Signal<boolean>;\n /** Whether the item may be ticked. Reactive when read from a template or a `computed`. */\n canSelect(item: T): boolean;\n /** Reactive when read from a template or a `computed`. An item that cannot be selected is never selected. */\n isSelected(item: T): boolean;\n /** Does nothing for an item that cannot be selected. */\n toggle(item: T, selected?: boolean): void;\n /** Ticks every selectable displayed item, or unticks them all when they already are. */\n toggleAll(): void;\n /** Turns the selection mode on or off; leaving it clears the selection. */\n toggleMode(): void;\n start(): void;\n /** Leaves the selection mode and clears the selection. */\n exit(): void;\n clear(): void;\n}\n\n/**\n * Multi-selection over a displayed list — the selection mode, the ticked\n * items, and the state of a select-all checkbox — shared by the workspace list\n * and the document list.\n *\n * Ticks are kept by key, so they survive the list being filtered or switched\n * between grid and list views: a tick on an item that is filtered out stays\n * until it comes back, but only displayed items are counted and returned.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectSelection<T>(options: SelectionOptions<T>): Selection<T> {\n assertInInjectionContext(injectSelection);\n\n const { items, key } = options;\n const canSelect = options.selectable ?? (() => true);\n const active = signal(false);\n const keys = signal<ReadonlySet<string>>(new Set());\n\n const selectableItems = computed(() => items().filter(canSelect));\n const selectedItems = computed(() => selectableItems().filter(item => keys().has(key(item))));\n const count = computed(() => selectedItems().length);\n // `length > 0` matters: [].every() is true, which would label an empty list \"Unselect all\".\n const allSelected = computed(() => selectableItems().length > 0 && count() === selectableItems().length);\n const partiallySelected = computed(() => count() > 0 && !allSelected());\n\n const clear = () => keys.set(new Set());\n\n return {\n active: active.asReadonly(),\n selectableItems,\n selectedItems,\n count,\n allSelected,\n partiallySelected,\n canSelect,\n isSelected: item => canSelect(item) && keys().has(key(item)),\n toggle: (item, selected) => {\n if (!canSelect(item)) return;\n const id = key(item);\n keys.update(current => {\n const next = new Set(current);\n const on = selected ?? !next.has(id);\n if (on) next.add(id);\n else next.delete(id);\n return next;\n });\n },\n toggleAll: () => {\n const displayed = selectableItems();\n if (allSelected()) {\n const ids = new Set(displayed.map(key));\n keys.update(current => new Set([...current].filter(id => !ids.has(id))));\n } else {\n keys.update(current => new Set([...current, ...displayed.map(key)]));\n }\n },\n toggleMode: () => {\n if (active()) {\n active.set(false);\n clear();\n } else {\n active.set(true);\n }\n },\n start: () => active.set(true),\n exit: () => {\n active.set(false);\n clear();\n },\n clear\n };\n}\n","import { fold, foldWithOffsets } from \"./text-match\";\n\n/** Elements whose text is not the document's own, or never shown. */\nconst SKIPPED = new Set([\"SCRIPT\", \"STYLE\", \"NOSCRIPT\", \"TEMPLATE\", \"HEAD\", \"TITLE\"]);\n\n/** Elements that start a new line of text: a match never reaches across one. */\nconst BLOCKS = new Set([\n \"ADDRESS\",\n \"ARTICLE\",\n \"ASIDE\",\n \"BLOCKQUOTE\",\n \"BR\",\n \"DD\",\n \"DIV\",\n \"DL\",\n \"DT\",\n \"FIGCAPTION\",\n \"FIGURE\",\n \"FOOTER\",\n \"H1\",\n \"H2\",\n \"H3\",\n \"H4\",\n \"H5\",\n \"H6\",\n \"HEADER\",\n \"HR\",\n \"LI\",\n \"MAIN\",\n \"NAV\",\n \"OL\",\n \"P\",\n \"PRE\",\n \"SECTION\",\n \"TABLE\",\n \"TD\",\n \"TH\",\n \"TR\",\n \"UL\"\n]);\n\n/** Stands between two blocks in the searched text. A query cannot contain it, so no match spans it. */\nconst BOUNDARY = \"\\u0000\";\n\nconst SHOW_TEXT = 4; // NodeFilter.SHOW_TEXT — the constant of the window the root belongs to may not be reachable\n\ninterface Segment {\n node: Text;\n /** Where this node's text starts in the concatenated, folded text. */\n from: number;\n length: number;\n /** For each folded unit, the slice of the node's own text it came from. */\n starts: number[];\n ends: number[];\n}\n\nfunction blockOf(node: Node, root: Node): Node {\n for (let element = node.parentElement; element && element !== root; element = element.parentElement) {\n if (BLOCKS.has(element.tagName)) return element;\n }\n return root;\n}\n\n/**\n * Every occurrence of `query` in the text under `root`, as DOM ranges — case- and\n * accent-insensitive, and across inline elements (`foo<b>bar</b>` holds \"foobar\").\n * The document itself is not touched: what to do with the ranges is the caller's\n * business (see `injectTextSearch`, which paints them with the CSS Custom Highlight API).\n *\n * The query is matched as typed, spaces included, against the text as the DOM holds\n * it: a run of whitespace in the source is not collapsed the way the browser draws it.\n */\nexport function findTextRanges(root: Node, query: string): Range[] {\n const needle = fold(query.trim());\n const doc = root.ownerDocument ?? (root as Document);\n if (!needle) return [];\n\n const segments: Segment[] = [];\n let folded = \"\";\n let previousBlock: Node | null = null;\n\n const walker = doc.createTreeWalker(root, SHOW_TEXT);\n for (let node = walker.nextNode() as Text | null; node; node = walker.nextNode() as Text | null) {\n const parent = node.parentElement;\n if (parent && SKIPPED.has(parent.tagName)) continue;\n const block = blockOf(node, root);\n if (previousBlock && block !== previousBlock) folded += BOUNDARY;\n previousBlock = block;\n\n const piece = foldWithOffsets(node.data);\n segments.push({ node, from: folded.length, length: piece.folded.length, starts: piece.starts, ends: piece.ends });\n folded += piece.folded;\n }\n\n const ranges: Range[] = [];\n let cursor = 0; // matches come in order, so the segment lookup only ever moves forward\n const segmentAt = (position: number): Segment => {\n while (cursor < segments.length - 1 && position >= segments[cursor].from + segments[cursor].length) cursor++;\n return segments[cursor];\n };\n\n for (let at = folded.indexOf(needle); at !== -1; at = folded.indexOf(needle, at + needle.length)) {\n cursor = 0;\n const first = segmentAt(at);\n const last = segmentAt(at + needle.length - 1);\n const range = doc.createRange();\n range.setStart(first.node, first.starts[at - first.from]);\n range.setEnd(last.node, last.ends[at + needle.length - 1 - last.from]);\n ranges.push(range);\n }\n return ranges;\n}\n","import { assertInInjectionContext, computed, DestroyRef, effect, inject, Signal, signal, untracked } from \"@angular/core\";\nimport { findTextRanges } from \"../utils/text-search\";\n\nexport const SEARCH_HIGHLIGHT = \"workspace-search\";\nexport const ACTIVE_HIGHLIGHT = \"workspace-search-active\";\n\nconst STYLE = `\n ::highlight(${SEARCH_HIGHLIGHT}) { background-color: rgba(250, 204, 21, 0.4); color: inherit; }\n ::highlight(${ACTIVE_HIGHLIGHT}) { background-color: rgba(249, 115, 22, 0.6); color: inherit; }\n`;\n\nexport interface TextSearchOptions {\n /** The node whose text is searched — the body of a same-origin iframe, say. `null` when there is nothing to search. */\n root: Signal<Node | null>;\n query: Signal<string>;\n /** How long the query must stay unchanged before the text is searched, in ms. */\n debounce?: number;\n}\n\nexport interface TextSearch {\n /** How many occurrences there are. */\n readonly matches: Signal<number>;\n /** The occurrence in focus (0-based), or -1 when there is none. */\n readonly activeIndex: Signal<number>;\n /** Whether the browser can paint the matches (the CSS Custom Highlight API); when it cannot, they are still counted and scrolled to. */\n readonly highlighting: Signal<boolean>;\n /** Moves to the next occurrence, wrapping round. */\n next(): void;\n previous(): void;\n}\n\n/** The painting half of the CSS Custom Highlight API, of the window the searched text lives in. */\nfunction highlighterOf(root: Node): { registry: HighlightRegistry; create: (ranges: Range[]) => Highlight; document: Document } | null {\n const document = root.ownerDocument ?? (root as Document);\n const win = document.defaultView as (Window & typeof globalThis) | null;\n const registry = win?.CSS?.highlights;\n if (!win || !registry || typeof win.Highlight !== \"function\") return null;\n return { registry, create: ranges => new win.Highlight(...ranges), document };\n}\n\n/** The rules that paint a highlight live in the document that holds the text — an iframe's own, not the host's. */\nfunction ensureStyle(document: Document): void {\n if (document.querySelector(\"style[data-workspace-search]\") || !document.head) return;\n const style = document.createElement(\"style\");\n style.setAttribute(\"data-workspace-search\", \"\");\n style.textContent = STYLE;\n document.head.appendChild(style);\n}\n\n/**\n * Find-in-text over a document you do not render through Angular, painted with the\n * CSS Custom Highlight API: the text is never touched, the matches are ranges the\n * browser draws.\n *\n * It follows the query (debounced) and the root: a new root is searched again, and\n * whatever was painted on the old one is taken back. Without the API (older\n * browsers) the occurrences are still counted and scrolled to.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectTextSearch({ root, query, debounce = 200 }: TextSearchOptions): TextSearch {\n assertInInjectionContext(injectTextSearch);\n\n const ranges = signal<Range[]>([]);\n const activeIndex = signal(-1);\n const highlighting = signal(false);\n const matches = computed(() => ranges().length);\n\n let painted: HighlightRegistry | null = null;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const unpaint = () => {\n painted?.delete(SEARCH_HIGHLIGHT);\n painted?.delete(ACTIVE_HIGHLIGHT);\n painted = null;\n };\n\n const paint = (node: Node | null, found: Range[], active: number) => {\n unpaint();\n const highlighter = node ? highlighterOf(node) : null;\n highlighting.set(!!highlighter);\n if (!highlighter || !found.length) return;\n ensureStyle(highlighter.document);\n highlighter.registry.set(SEARCH_HIGHLIGHT, highlighter.create(found));\n if (active >= 0) highlighter.registry.set(ACTIVE_HIGHLIGHT, highlighter.create([found[active]]));\n painted = highlighter.registry;\n };\n\n const reveal = (range: Range | undefined) => {\n // The container of the text, not the text: a node cannot be scrolled to.\n range?.startContainer.parentElement?.scrollIntoView?.({ block: \"center\" });\n };\n\n const run = () => {\n const node = root();\n const found = node ? findTextRanges(node, query()) : [];\n ranges.set(found);\n activeIndex.set(found.length ? 0 : -1);\n paint(node, found, activeIndex());\n reveal(found[0]);\n };\n\n effect(() => {\n root();\n const value = query();\n clearTimeout(timer);\n // An emptied box clears at once; typing waits for a pause.\n timer = setTimeout(() => untracked(run), value.trim() ? debounce : 0);\n });\n\n inject(DestroyRef).onDestroy(() => {\n clearTimeout(timer);\n unpaint();\n });\n\n const move = (step: 1 | -1) => {\n const count = ranges().length;\n if (!count) return;\n const next = (activeIndex() + step + count) % count;\n activeIndex.set(next);\n paint(root(), ranges(), next);\n reveal(ranges()[next]);\n };\n\n return { matches, activeIndex: activeIndex.asReadonly(), highlighting: highlighting.asReadonly(), next: () => move(1), previous: () => move(-1) };\n}\n","import { assertInInjectionContext, computed, Signal, signal } from \"@angular/core\";\nimport type { Workspace } from \"../models/workspace.model\";\nimport { matchesText } from \"../utils/text-match\";\nimport { canWrite } from \"../utils/workspace-role\";\n\n/** `editor`: the workspaces you can change (creator or owner); `reader`: those shared with you read-only. */\nexport type WorkspaceTab = \"all\" | \"editor\" | \"reader\";\n\nexport interface WorkspaceTabCounts {\n all: number;\n editor: number;\n reader: number;\n}\n\n/** What the search box looks at: the name only, or the name and the description. */\nexport type WorkspaceSearchFields = \"name\" | \"name+description\";\n\nexport interface WorkspaceFilterOptions {\n /** What the query is matched against. Read reactively; the name only when absent. */\n fields?: () => WorkspaceSearchFields;\n}\n\n/**\n * The workspace list's tab and search box, and the list they produce.\n *\n * The tab badges count the whole list: typing in the search box narrows what is\n * shown, not the numbers next to the tabs.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectWorkspaceFilter(workspaces: Signal<readonly Workspace[]>, options: WorkspaceFilterOptions = {}) {\n assertInInjectionContext(injectWorkspaceFilter);\n\n const tab = signal<WorkspaceTab>(\"all\");\n const query = signal(\"\");\n\n const counts = computed<WorkspaceTabCounts>(() => {\n const editor = workspaces().filter(w => canWrite(w.currentUserRole)).length;\n return { all: workspaces().length, editor, reader: workspaces().length - editor };\n });\n\n const filtered = computed(() =>\n workspaces().filter(w => {\n const inTab = tab() === \"all\" || (tab() === \"editor\") === canWrite(w.currentUserRole);\n const inQuery = matchesText(w.name, query()) || (options.fields?.() === \"name+description\" && matchesText(w.description, query()));\n return inTab && inQuery;\n })\n );\n\n return {\n tab,\n query,\n counts,\n filtered,\n clearQuery: () => query.set(\"\")\n };\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { splitByMatch } from \"../../utils/text-match\";\n\n/**\n * A text with what a search matched marked, ignoring case and accents.\n *\n * Rendered chunk by chunk through Angular's own escaping: the text is a user's\n * workspace or document name, and interpolating it into markup would execute\n * anything HTML-ish it contains. The template is on one line on purpose, so no\n * whitespace sneaks in between the chunks.\n */\n@Component({\n selector: \"highlighted-text\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"contents\" },\n template: `@for (chunk of chunks(); track $index) {@if (chunk.match) {<mark class=\"rounded-sm bg-[rgba(234,179,8,0.25)] text-inherit\">{{ chunk.text }}</mark>} @else {<span>{{ chunk.text }}</span>}}`\n})\nexport class HighlightedText {\n readonly text = input.required<string>();\n readonly query = input(\"\");\n\n protected readonly chunks = computed(() => splitByMatch(this.text(), this.query()));\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { UserIcon, UserRoundIcon } from \"@sinequa/galactik\";\nimport type { WorkspaceRole } from \"../../models/workspace.model\";\nimport { canWrite } from \"../../utils/workspace-role\";\n\n/** What the workspace is to you: yours (\"Owner\", for a creator or owner) or shared with you (\"Shared\"). */\n@Component({\n selector: \"workspace-role-tag\",\n imports: [TranslocoPipe, UserIcon, UserRoundIcon],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"inline-flex items-center gap-1 rounded bg-(--bg-indigo-base-alt) px-1 py-0.5 text-xs text-(--font-indigo-base)\",\n \"[attr.data-owner]\": \"owned() ? '' : null\"\n },\n template: `\n @if (owned()) {\n <user-icon class=\"size-3\" />\n {{ \"documentWorkspaces.list.roleOwner\" | transloco }}\n } @else {\n <user-round-icon class=\"size-3\" />\n {{ \"documentWorkspaces.list.roleShared\" | transloco }}\n }\n `\n})\nexport class WorkspaceRoleTag {\n readonly role = input.required<WorkspaceRole>();\n\n protected readonly owned = computed(() => canWrite(this.role()));\n}\n","import { ApiError } from \"@sinequa/atomic\";\n\n/**\n * Either a translation key (under the `documentWorkspaces` scope) or text to\n * show verbatim. The split keeps this module free of any translation engine:\n * the view passes `key` to the `transloco` pipe and prints `text` as is.\n */\nexport type WorkspaceErrorMessage = { key: string } | { text: string };\n\n/**\n * Maps a caught error to the message shown in an inline error banner.\n *\n * Only the statuses we recognise get a catalog entry. The default branch\n * deliberately passes the server's own prose through untranslated (a 409\n * `container_locked` has no dedicated error class and its message is already\n * human-readable): localizing the rest would need the API to return stable\n * error codes instead of English sentences.\n *\n * @param err - The caught value, of any type.\n */\nexport function workspaceErrorMessage(err: unknown): WorkspaceErrorMessage {\n if (err instanceof ApiError) {\n switch (err.status) {\n case 401:\n return { key: \"documentWorkspaces.errors.signIn\" };\n case 403:\n return { key: \"documentWorkspaces.errors.forbidden\" };\n case 404:\n return { key: \"documentWorkspaces.errors.notFound\" };\n default:\n return err.message ? { text: err.message } : { key: \"documentWorkspaces.errors.generic\" };\n }\n }\n return err instanceof Error && err.message ? { text: err.message } : { key: \"documentWorkspaces.errors.generic\" };\n}\n","import { afterNextRender, ChangeDetectionStrategy, Component, computed, signal, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n createCallable,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogResult,\n DialogTitleComponent,\n injectCallRef\n} from \"@sinequa/galactik\";\nimport { workspaceErrorMessage, WorkspaceErrorMessage } from \"../../utils/error-message\";\nimport { splitByMatch, TextChunk } from \"../../utils/text-match\";\n\nexport interface ConfirmDialogConfig {\n /** Already translated. */\n title: string;\n /** Already translated, with the subject interpolated. */\n description?: string;\n /** A substring of `description` to emphasise — typically the name of what is about to be deleted. */\n emphasis?: string;\n /** Already translated. Defaults to \"Delete\" when `destructive`, \"Save\" otherwise. */\n confirmLabel?: string;\n /** Already translated; shown on the confirm button while `action` runs. Defaults to \"Deleting…\" when `destructive`. */\n busyLabel?: string;\n /** Already translated. Defaults to \"Cancel\". */\n cancelLabel?: string;\n /** Styles the confirm button as a destructive action. */\n destructive?: boolean;\n /**\n * What confirming does. While it runs the dialog cannot be dismissed (Cancel,\n * Escape), so a request cannot be abandoned half-way through; if it rejects,\n * the dialog stays open with the error so it can be retried. Without it,\n * confirming just closes the dialog with `dialog-confirm`.\n */\n action?: () => Promise<unknown>;\n}\n\n/** Text to print as is, or a key of the `documentWorkspaces` scope to translate. */\ntype Label = { key: string } | { text: string };\n\n/**\n * The one confirmation dialog — \"delete these workspaces?\", \"delete this\n * document?\" — instead of a modal per use.\n *\n * ```ts\n * const result = await ConfirmAction.call({\n * title, description, emphasis: name, destructive: true,\n * action: () => store.remove([id])\n * });\n * if (result === \"dialog-confirm\") …\n * ```\n *\n * Resolves `dialog-confirm` once the action succeeded, `dialog-cancel` otherwise.\n */\n@Component({\n selector: \"workspace-confirm-dialog\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogTitleComponent\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <dialog #dialog (cancel)=\"onNativeCancel($event)\" (closed)=\"call.end($event)\">\n <DialogContent size=\"md\">\n <!-- The header's close button cannot be disabled, so the whole header goes inert while the action runs. -->\n <DialogHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\" [attr.inert]=\"busy() ? '' : null\">\n <DialogTitle>{{ config().title }}</DialogTitle>\n </DialogHeader>\n\n @if (config().description || error()) {\n <DialogBody>\n @if (config().description) {\n <p class=\"text-sm\">\n @for (chunk of descriptionChunks(); track $index) {\n @if (chunk.match) {\n <strong>{{ chunk.text }}</strong>\n } @else {\n {{ chunk.text }}\n }\n }\n </p>\n }\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">\n @if (\"key\" in message) {\n {{ message.key | transloco }}\n } @else {\n {{ message.text }}\n }\n </p>\n }\n </DialogBody>\n }\n\n <DialogFooter>\n <button variant=\"secondary\" [disabled]=\"busy()\" (click)=\"dialog.close('dialog-cancel')\">\n @let cancel = cancelLabel();\n @if (\"key\" in cancel) {\n {{ cancel.key | transloco }}\n } @else {\n {{ cancel.text }}\n }\n </button>\n <button\n variant=\"primary\"\n [class]=\"config().destructive ? 'bg-(--bg-error-base) enabled:hover:bg-(--bg-error-base) enabled:hover:opacity-90' : ''\"\n [disabled]=\"busy()\"\n (click)=\"confirm()\">\n @let confirmation = confirmLabel();\n @if (\"key\" in confirmation) {\n {{ confirmation.key | transloco }}\n } @else {\n {{ confirmation.text }}\n }\n </button>\n </DialogFooter>\n </DialogContent>\n </dialog>\n `\n})\nexport class ConfirmDialog {\n protected readonly call = injectCallRef<ConfirmDialogConfig, DialogResult>();\n\n private readonly dialog = viewChild.required(DialogComponent);\n\n protected readonly config = computed(() => this.call.props());\n protected readonly busy = signal(false);\n protected readonly error = signal<WorkspaceErrorMessage | null>(null);\n\n protected readonly descriptionChunks = computed<TextChunk[]>(() => {\n const { description = \"\", emphasis } = this.config();\n // Only the first occurrence is emphasised: the subject appears once in the sentence.\n let emphasised = false;\n return splitByMatch(description, emphasis ?? \"\").map(chunk => {\n if (!chunk.match || emphasised) return { ...chunk, match: false };\n emphasised = true;\n return chunk;\n });\n });\n\n protected readonly cancelLabel = computed<Label>(() => {\n const { cancelLabel } = this.config();\n return cancelLabel ? { text: cancelLabel } : { key: \"documentWorkspaces.common.cancel\" };\n });\n\n protected readonly confirmLabel = computed<Label>(() => {\n const { confirmLabel, busyLabel, destructive } = this.config();\n if (this.busy() && busyLabel) return { text: busyLabel };\n if (this.busy() && destructive && !confirmLabel) return { key: \"documentWorkspaces.common.deleting\" };\n if (confirmLabel) return { text: confirmLabel };\n return { key: destructive ? \"documentWorkspaces.common.delete\" : \"documentWorkspaces.common.save\" };\n });\n\n constructor() {\n afterNextRender(() => this.dialog().showModal());\n }\n\n protected async confirm(): Promise<void> {\n const { action } = this.config();\n if (!action) {\n this.dialog().close(\"dialog-confirm\");\n return;\n }\n\n this.busy.set(true);\n this.error.set(null);\n try {\n await action();\n } catch (err) {\n this.error.set(workspaceErrorMessage(err));\n return;\n } finally {\n this.busy.set(false);\n }\n this.dialog().close(\"dialog-confirm\");\n }\n\n /** Escape on a native <dialog> closes it: refuse while the action is running. */\n protected onNativeCancel(event: Event): void {\n if (this.busy()) event.preventDefault();\n }\n}\n\n/** Imperative entry point: `await ConfirmAction.call({ title, destructive: true, action })`. */\nexport const ConfirmAction = createCallable<ConfirmDialogConfig, DialogResult>(ConfirmDialog);\n","import type { TrackedUpload } from \"../models/upload.model\";\n\n/**\n * Text to print as is, or a key of the `documentWorkspaces` scope (with its\n * parameters) to translate. Lets a class decide *what* to say and leave *saying\n * it* to the template, which has the translation pipe.\n */\nexport type Message = { text: string } | { key: string; params?: Record<string, unknown> };\n\n/** A message from the `tracker` namespace. */\nexport function trackerMessage(key: string, params?: Record<string, unknown>): Message {\n return { key: `documentWorkspaces.tracker.${key}`, params };\n}\n\n/** Shortens a file name to `max` characters, keeping its extension: `long-na….pdf`. */\nexport function truncateName(name: string, max: number): string {\n if (name.length <= max) return name;\n const dot = name.lastIndexOf(\".\");\n if (dot > 0 && name.length - dot <= 5) {\n const extension = name.slice(dot);\n return `${name.slice(0, max - extension.length - 1)}…${extension}`;\n }\n return `${name.slice(0, max - 1)}…`;\n}\n\n/**\n * What to call a tracked row: the file's name — or, for a job started elsewhere whose files\n * the status API does not list, whose job it is.\n */\nexport function uploadLabel(upload: Pick<TrackedUpload, \"name\" | \"remote\">, max: number): Message {\n if (upload.name) return { text: truncateName(upload.name, max) };\n return trackerMessage(upload.remote?.mine ? \"ownUpload\" : \"remoteUpload\");\n}\n\n/** `m:ss` for a duration in ms, clamped at 0 so a late tick cannot go negative. */\nexport function formatClock(ms: number): string {\n const seconds = Math.max(0, Math.round(ms / 1000));\n return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, \"0\")}`;\n}\n","import { ChangeDetectionStrategy, Component, input } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport type { Message } from \"../../utils/message\";\n\n/** Prints a {@link Message}: its text as is, or its key translated. */\n@Component({\n selector: \"workspace-message\",\n imports: [TranslocoPipe],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"contents\" },\n template: `\n @let m = message();\n @if (\"key\" in m) {\n {{ m.key | transloco: m.params }}\n } @else {\n {{ m.text }}\n }\n `\n})\nexport class WorkspaceMessage {\n readonly message = input.required<Message>();\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport type { DocumentStatus as Status } from \"../../models/workspace.model\";\nimport { Message, trackerMessage } from \"../../utils/message\";\nimport { WorkspaceMessage } from \"./workspace-message\";\n\n/**\n * The badge of a document that is not finished: \"1/2 Uploading\", \"2/2 Indexing\"\n * (with animated dots while it moves) or \"Upload failed\". Renders nothing for a\n * document that is indexed, or whose state is not known: the card shows its date.\n */\n@Component({\n selector: \"document-status-badge\",\n imports: [WorkspaceMessage],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"inline-flex items-center gap-1 text-xs data-[status=error]:text-(--font-error-base)\",\n \"[hidden]\": \"!message()\",\n \"[attr.data-status]\": \"status() ?? null\"\n },\n template: `\n @if (message(); as message) {\n <workspace-message [message]=\"message\" />\n @if (status() !== \"error\") {\n <span aria-hidden=\"true\" class=\"flex gap-0.5 motion-safe:animate-pulse\"><i class=\"bg-current size-1 rounded-full\"></i><i class=\"bg-current size-1 rounded-full\"></i><i class=\"bg-current size-1 rounded-full\"></i></span>\n }\n }\n `\n})\nexport class DocumentStatusBadge {\n /** `null` or absent once the document is indexed. */\n readonly status = input<Status | null | undefined>();\n\n protected readonly message = computed<Message | null>(() => {\n switch (this.status()) {\n case \"uploading\":\n return trackerMessage(\"stageUploading\");\n case \"indexing\":\n return trackerMessage(\"stageIndexing\");\n case \"error\":\n return trackerMessage(\"uploadFailed\");\n default:\n return null;\n }\n });\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { FileIcon, FileSpreadsheetIcon, FileTextIcon } from \"@sinequa/galactik\";\nimport type { DocumentType } from \"../../models/workspace.model\";\n\n// Full class names: Tailwind only generates the ones it can read as literals.\nconst TYPE_CLASSES: Record<DocumentType, string> = {\n pdf: \"bg-(--red-100,#FFDEDF) text-(--red-600,#AD3739)\",\n csv: \"bg-(--green-100,#E5EEE3) text-(--green-600,#3E6936)\",\n doc: \"bg-(--blue-100,#E2EDF4) text-(--blue-600,#45677E)\"\n};\n\n/** The icon of a document, on a tile tinted by the family `getDocumentType()` puts its file name in. */\n@Component({\n selector: \"document-type-icon\",\n imports: [FileIcon, FileSpreadsheetIcon, FileTextIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"inline-flex shrink-0 items-center justify-center rounded-lg\",\n \"[class]\": \"classes()\",\n \"[attr.data-type]\": \"type()\"\n },\n template: `\n @switch (type()) {\n @case (\"csv\") {\n <file-spreadsheet-icon class=\"size-5 opacity-80\" />\n }\n @case (\"pdf\") {\n <file-text-icon class=\"size-5 opacity-80\" />\n }\n @default {\n <file-icon class=\"size-5 opacity-80\" />\n }\n }\n `\n})\nexport class DocumentTypeIcon {\n readonly type = input.required<DocumentType>();\n /** `lg` (36px) on a card, `md` (32px) in a row. */\n readonly size = input<\"md\" | \"lg\">(\"lg\");\n\n protected readonly classes = computed(() => `${TYPE_CLASSES[this.type()]} ${this.size() === \"lg\" ? \"size-9\" : \"size-8\"}`);\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { FolderIcon } from \"@sinequa/galactik\";\nimport type { WorkspaceColor } from \"../../models/workspace.model\";\n\n// Full class names, never built from the colour: Tailwind only generates the\n// classes it can read as literals. A tint of the ramp for the tile, a stronger one\n// for the folder; the hex is the fallback for a theme that lacks the ramp.\nconst COLOR_CLASSES: Record<WorkspaceColor, string> = {\n default: \"bg-(--yellow-100,#FDF2DC) text-(--yellow-400,#F6CA72)\",\n teal: \"bg-(--teal-100,#E0F4F1) text-(--teal-400,#4EBFB0)\",\n pink: \"bg-(--pink-100,#FAECFB) text-(--pink-400,#C9AACB)\",\n blue: \"bg-(--blue-100,#E2EDF4) text-(--blue-400,#8CB8D5)\",\n orange: \"bg-(--orange-100,#FFEEE2) text-(--orange-400,#FFBA8D)\",\n indigo: \"bg-(--indigo-100,#E5E9F9) text-(--indigo-400,#98A9E5)\",\n green: \"bg-(--green-100,#E5EEE3) text-(--green-400,#709B67)\"\n};\n\n/** A workspace's folder on a tile tinted with the accent derived from its id (see `workspaceColorFor()`). */\n@Component({\n selector: \"workspace-icon\",\n imports: [FolderIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"inline-flex shrink-0 items-center justify-center rounded-(--radius-md)\",\n \"[class]\": \"classes()\",\n \"[attr.data-color]\": \"color()\"\n },\n template: `<folder-icon class=\"size-5 opacity-80\" />`\n})\nexport class WorkspaceIcon {\n readonly color = input<WorkspaceColor>(\"default\");\n /** `lg` (38px) on a card, `md` (32px) in a row. */\n readonly size = input<\"md\" | \"lg\">(\"lg\");\n\n protected readonly classes = computed(() => `${COLOR_CLASSES[this.color()]} ${this.size() === \"lg\" ? \"size-[38px]\" : \"size-8\"}`);\n}\n","import { ChangeDetectionStrategy, Component, input } from \"@angular/core\";\nimport { getDocumentType } from \"../../utils/doc-type\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\n\n/** The files staged for upload, before anything is sent. */\n@Component({\n selector: \"upload-file-list\",\n imports: [DocumentTypeIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { role: \"list\", class: \"flex max-h-48 flex-col gap-1 overflow-y-auto\" },\n template: `\n @for (file of files(); track file.name) {\n <div role=\"listitem\" class=\"flex items-center gap-2 text-sm\">\n <document-type-icon [type]=\"typeOf(file)\" />\n <!-- Rendered through Angular's escaping: a file name is user-controlled. -->\n <span class=\"truncate\" [attr.title]=\"file.name\">{{ file.name }}</span>\n </div>\n }\n `\n})\nexport class UploadFileList {\n readonly files = input.required<readonly File[]>();\n\n protected typeOf(file: File) {\n return getDocumentType(file.name);\n }\n}\n","import { afterNextRender, ChangeDetectionStrategy, Component, computed, inject, signal, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n createCallable,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogResult,\n DialogTitleComponent,\n injectCallRef\n} from \"@sinequa/galactik\";\nimport { Dropzone } from \"../../directives/dropzone.directive\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { UploadService } from \"../../upload/upload.service\";\nimport { canWrite } from \"../../utils/workspace-role\";\nimport { UploadFileList } from \"./upload-file-list\";\n\nexport interface UploadDialogConfig {\n /** The workspace the files go to. */\n workspace: Pick<Workspace, \"id\" | \"name\" | \"currentUserRole\">;\n /** The tus endpoint — the host's `upload-endpoint`. */\n endpoint: string;\n}\n\n/**\n * \"Upload Documents\": stage files by dropping or picking them, then send them all\n * as one batch.\n *\n * Staging only — nothing leaves the browser until Upload is pressed, so the whole\n * selection goes out as ONE ingest job instead of each drop starting its own.\n * Dismissing the dialog any other way discards the selection: nothing was sent,\n * so there is nothing to keep.\n *\n * ```ts\n * await UploadDocuments.call({ workspace, endpoint });\n * ```\n *\n * Refuses to open for someone who cannot write to the workspace: the Upload\n * button is hidden for them, this is the belt to that braces.\n */\n@Component({\n selector: \"upload-dialog\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n DialogComponent,\n DialogBodyComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogTitleComponent,\n Dropzone,\n UploadFileList\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <dialog #dialog (closed)=\"call.end($event)\">\n <DialogContent size=\"md\">\n <DialogHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\">\n <DialogTitle>{{ \"documentWorkspaces.upload.modalTitle\" | transloco }}</DialogTitle>\n </DialogHeader>\n\n <DialogBody>\n <div\n dropzone\n (filesSelected)=\"stage($event)\"\n [attr.aria-label]=\"'documentWorkspaces.upload.dropzoneLabel' | transloco\"\n class=\"border-(--stroke-primary-lighter) hover:border-(--stroke-primary-base) data-dragover:border-(--stroke-primary-base) data-dragover:bg-(--bg-primary-lightest) focus-visible:ring-(--stroke-primary-base) cursor-pointer rounded-xl border-2 border-dashed p-8 text-center text-sm transition-colors outline-none focus-visible:ring-2\">\n {{ \"documentWorkspaces.upload.dropPrefix\" | transloco }}\n <span class=\"text-(--font-primary-base) underline\">{{ \"documentWorkspaces.upload.browse\" | transloco }}</span>\n </div>\n\n @if (files().length) {\n <upload-file-list [files]=\"files()\" />\n }\n </DialogBody>\n\n <DialogFooter>\n <button variant=\"secondary\" (click)=\"dialog.close('dialog-cancel')\">\n {{ \"documentWorkspaces.common.cancel\" | transloco }}\n </button>\n <button [disabled]=\"!files().length\" (click)=\"upload()\">\n @if (files().length) {\n {{ \"documentWorkspaces.upload.uploadCount\" | transloco: { count: files().length } }}\n } @else {\n {{ \"documentWorkspaces.detail.upload\" | transloco }}\n }\n </button>\n </DialogFooter>\n </DialogContent>\n </dialog>\n `\n})\nexport class UploadDialog {\n protected readonly call = injectCallRef<UploadDialogConfig, DialogResult>();\n\n private readonly dialog = viewChild.required(DialogComponent);\n private readonly uploads = inject(UploadService);\n\n protected readonly files = signal<File[]>([]);\n\n /** Whether there is anything to send. */\n protected readonly ready = computed(() => this.files().length > 0);\n\n constructor() {\n afterNextRender(() => {\n if (!canWrite(this.call.props().workspace.currentUserRole)) {\n this.call.end(\"dialog-cancel\");\n return;\n }\n this.dialog().showModal();\n });\n }\n\n /**\n * Adds files to the staged selection. No gatekeeping here either: no type, count\n * or size check. Same-name files are still collapsed, and for a hard reason: a\n * document is addressed as `<jobId>/<fileName>`, so two files sharing a name\n * inside one job would collide on the server, not just in the list.\n */\n protected stage(files: File[]): void {\n this.files.update(staged => {\n const names = new Set(staged.map(f => f.name));\n const added = files.filter(f => !names.has(f.name) && names.add(f.name));\n return added.length ? [...staged, ...added] : staged;\n });\n }\n\n protected upload(): void {\n if (!this.ready()) return;\n const { workspace, endpoint } = this.call.props();\n this.uploads.addFiles(workspace, this.files(), { endpoint });\n this.dialog().close(\"dialog-confirm\");\n }\n}\n\n/** Imperative entry point: `await UploadDocuments.call({ workspace, endpoint })`. */\nexport const UploadDocuments = createCallable<UploadDialogConfig, DialogResult>(UploadDialog);\n","import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input, output, signal } from \"@angular/core\";\nimport type { ConnectionNotice } from \"../../models/upload.model\";\nimport { formatClock, Message, trackerMessage } from \"../../utils/message\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\nimport { ButtonComponent } from \"@sinequa/galactik\";\n\n/**\n * What the tracker says when the connection or the session is in trouble: what\n * happened, what is being done about it, and a countdown.\n *\n * It exists only while there is something to say, so its one-second clock only\n * runs then.\n */\n@Component({\n selector: \"connection-banner\",\n imports: [WorkspaceMessage, ButtonComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { role: \"status\", class: \"flex flex-col gap-1 border-b border-(--stroke-primary-lighter) bg-(--bg-error-lightest) px-4 py-3 text-sm\" },\n template: `\n <div class=\"font-medium\"><workspace-message [message]=\"title()\" /></div>\n <div class=\"text-(--font-primary-muted)\"><workspace-message [message]=\"detail()\" /></div>\n <div class=\"text-(--font-primary-muted) text-xs\" data-testid=\"countdown\">\n @for (part of countdown(); track $index) {\n @if ($index > 0) {\n <span aria-hidden=\"true\"> · </span>\n }\n <workspace-message [message]=\"part\" />\n }\n </div>\n @if (showRetry()) {\n <button variant=\"secondary\" size=\"sm\" class=\"self-start\" (click)=\"retry.emit()\">\n <workspace-message [message]=\"retryLabel\" />\n </button>\n }\n `\n})\nexport class ConnectionBanner {\n readonly notice = input.required<ConnectionNotice>();\n /** The Retry button was pressed — offered once signed out, when the user can act. */\n readonly retry = output<void>();\n\n protected readonly retryLabel: Message = { key: \"documentWorkspaces.common.retry\" };\n\n private readonly now = signal(Date.now());\n\n constructor() {\n const timer = setInterval(() => this.now.set(Date.now()), 1000);\n inject(DestroyRef).onDestroy(() => clearInterval(timer));\n }\n\n protected readonly title = computed<Message>(() => {\n switch (this.notice().phase) {\n case \"reconnecting\":\n return trackerMessage(\"reconnecting\");\n case \"signed-out\":\n return trackerMessage(\"signedOut\");\n default:\n return trackerMessage(\"connectionLost\");\n }\n });\n\n protected readonly detail = computed<Message>(() => {\n const { phase, uploads, attempt, maxAttempts } = this.notice();\n if (phase === \"reconnecting\") return trackerMessage(\"restoringSession\");\n if (phase === \"signed-out\") return uploads ? trackerMessage(\"uploadsPaused\", { count: uploads }) : trackerMessage(\"signInAgain\");\n return uploads\n ? trackerMessage(\"retryAttemptWithFiles\", { attempt, max: maxAttempts, count: uploads })\n : trackerMessage(\"retryAttempt\", { attempt, max: maxAttempts });\n });\n\n /** \"Next try in 0:03 · lost 0:12 ago · gives up in 4:40\", the parts that apply. */\n protected readonly countdown = computed<Message[]>(() => {\n const { phase, retryAtMs, budgetEndsAtMs, sinceMs } = this.notice();\n const now = this.now();\n const parts: Message[] = [];\n if (phase === \"retrying\" && retryAtMs !== undefined) {\n const left = retryAtMs - now;\n parts.push(left > 0 ? trackerMessage(\"nextTryIn\", { time: formatClock(left) }) : trackerMessage(\"tryingNow\"));\n }\n parts.push(trackerMessage(\"lostAgo\", { time: formatClock(now - sinceMs) }));\n if (phase === \"retrying\" && budgetEndsAtMs !== undefined) parts.push(trackerMessage(\"givesUpIn\", { time: formatClock(budgetEndsAtMs - now) }));\n return parts;\n });\n\n protected readonly showRetry = computed(() => this.notice().phase === \"signed-out\" && !!this.notice().retry);\n}\n","import { ChangeDetectionStrategy, Component, ViewEncapsulation } from \"@angular/core\";\n\n/**\n * Three dots that light up one after the other: something is going on.\n *\n * The keyframes ship with the component (a library has no stylesheet of its own to\n * put them in); encapsulation is turned off for them to reach the dots, and their\n * names carry the component's so they cannot collide with the host page's.\n */\n@Component({\n selector: \"activity-dots\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: { class: \"inline-flex gap-px\", \"aria-hidden\": \"true\" },\n styles: `\n @keyframes activity-dots-appear {\n 0%,\n 60%,\n 100% {\n opacity: 0;\n }\n 30% {\n opacity: 1;\n }\n }\n activity-dots > span {\n display: inline-block;\n opacity: 0;\n animation: activity-dots-appear 1.4s infinite;\n }\n activity-dots > span:nth-child(2) {\n animation-delay: 0.2s;\n }\n activity-dots > span:nth-child(3) {\n animation-delay: 0.4s;\n }\n @media (prefers-reduced-motion: reduce) {\n activity-dots > span {\n animation: none;\n opacity: 1;\n }\n }\n `,\n template: `<span>.</span><span>.</span><span>.</span>`\n})\nexport class ActivityDots {}\n\n/** A ring that turns: something is going on. */\n@Component({\n selector: \"activity-spinner\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: { class: \"inline-block shrink-0 rounded-full border-2 border-(--bg-primary-light) border-t-(--stroke-secondary-base)\", \"aria-hidden\": \"true\" },\n styles: `\n @keyframes activity-spinner-turn {\n to {\n transform: rotate(360deg);\n }\n }\n activity-spinner {\n animation: activity-spinner-turn 0.85s linear infinite;\n }\n @media (prefers-reduced-motion: reduce) {\n activity-spinner {\n animation-duration: 3s;\n }\n }\n `,\n template: ``\n})\nexport class ActivitySpinner {}\n","import { ChangeDetectionStrategy, Component, computed, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckIcon, ChevronDownIcon, CircleXIcon, RefreshIcon, SyncIcon, UploadIcon, XMarkIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport type { TrackedUpload, UploadStatus } from \"../../models/upload.model\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\nimport { ActivityDots } from \"../shared/activity-indicators\";\n\ninterface SectionConfig {\n titleKey: string;\n /** Shows the average progress as a bar, and animated dots next to the title. */\n progress: boolean;\n /** Can be folded away — only the two sections that move. */\n foldable: boolean;\n}\n\n// Keys are spelled out, never built from the status: they are read by tooling.\nconst SECTIONS: Record<UploadStatus, SectionConfig> = {\n uploading: { titleKey: \"documentWorkspaces.tracker.sectionUploading\", progress: true, foldable: true },\n indexing: { titleKey: \"documentWorkspaces.tracker.sectionIndexing\", progress: true, foldable: true },\n done: { titleKey: \"documentWorkspaces.tracker.sectionCompleted\", progress: false, foldable: false },\n error: { titleKey: \"documentWorkspaces.tracker.sectionFailed\", progress: false, foldable: false },\n rejected: { titleKey: \"documentWorkspaces.tracker.sectionNotIndexed\", progress: false, foldable: false }\n};\n\n/**\n * The files of one status, for one workspace, in the tracker's expanded body:\n * a title, an average progress bar while they move, and a row per file with what\n * can be done to it.\n *\n * The files fold away and open again, and the bar follows the progress, with a\n * transition rather than a jump.\n *\n * It only reports what the user asked for; `UploadTracker` does it.\n */\n@Component({\n selector: \"upload-tracker-section\",\n imports: [\n TranslocoPipe,\n ActivityDots,\n ButtonComponent,\n CheckIcon,\n ChevronDownIcon,\n CircleXIcon,\n DocumentTypeIcon,\n RefreshIcon,\n SyncIcon,\n TooltipDirective,\n UploadIcon,\n XMarkIcon\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"block px-4 py-3 data-[status=error]:bg-(--bg-error-lightest) data-[status=rejected]:bg-(--bg-error-lightest)\",\n \"[attr.data-status]\": \"status()\"\n },\n template: `\n <div class=\"flex items-center gap-2\">\n <span class=\"flex shrink-0 items-center text-(--font-neutral-muted)\" [class.text-(--font-success-base)]=\"status() === 'done'\" [class.text-(--font-error-base)]=\"failed()\">\n @switch (status()) {\n @case (\"uploading\") {\n <upload-icon class=\"size-4\" />\n }\n @case (\"indexing\") {\n <sync-icon class=\"size-4\" />\n }\n @case (\"done\") {\n <check-icon class=\"size-4\" />\n }\n @default {\n <circle-x-icon class=\"size-4\" />\n }\n }\n </span>\n <span class=\"inline-flex shrink-0 items-baseline text-xs text-(--font-neutral-black)\" [class.text-(--font-error-base)]=\"failed()\">\n {{ config().titleKey | transloco }}\n @if (config().progress) {\n <activity-dots />\n }\n </span>\n @if (config().progress) {\n <div\n role=\"progressbar\"\n [attr.aria-valuenow]=\"average()\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n class=\"h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-(--bg-primary-lighter)\">\n <div class=\"h-full rounded-full bg-(--bg-primary-base) transition-[width] duration-[400ms] ease-[ease] motion-reduce:transition-none\" [style.width.%]=\"average()\"></div>\n </div>\n } @else {\n <span class=\"flex-1\"></span>\n }\n <span\n class=\"rounded-full bg-(--bg-neutral-base) px-2 text-xs text-(--font-neutral-base)\"\n [class.bg-(--bg-success-base-alt)]=\"status() === 'done'\"\n [class.text-(--font-success-base)]=\"status() === 'done'\"\n [class.bg-(--bg-error-base-alt)]=\"failed()\"\n [class.text-(--font-error-base)]=\"failed()\">\n {{ uploads().length }}\n </span>\n @if (config().foldable) {\n <button\n class=\"shrink-0\"\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-label]=\"'documentWorkspaces.tracker.toggleSection' | transloco\"\n [tooltip]=\"(open() ? 'documentWorkspaces.common.collapse' : 'documentWorkspaces.common.expand') | transloco\"\n (click)=\"toggle.emit()\">\n <chevron-down-icon class=\"transition-transform duration-[250ms] ease-[ease]\" [class.-rotate-180]=\"open()\" />\n </button>\n }\n </div>\n\n <!-- Folding keeps the rows in place and squeezes them away, which is what lets it be seen. -->\n <div\n class=\"max-h-0 overflow-hidden pt-0 opacity-0 transition-[max-height,opacity,padding] duration-300 ease-[ease] motion-reduce:transition-none data-open:max-h-[600px] data-open:pt-2 data-open:opacity-100\"\n [attr.data-open]=\"open() || !config().foldable ? '' : null\"\n [attr.inert]=\"open() || !config().foldable ? null : ''\">\n <ul class=\"flex flex-col\">\n @for (upload of uploads(); track upload.id) {\n <li class=\"flex items-center gap-3 py-2 not-first:border-t not-first:border-(--stroke-primary-lighter)\" [attr.data-status]=\"upload.status\">\n <document-type-icon class=\"shrink-0\" [type]=\"upload.type\" />\n <!-- Text, never markup: a file name and a server message are user-controlled. -->\n <span\n class=\"min-w-0 flex-1 truncate text-xs text-(--font-neutral-black)\"\n [class.text-(--font-error-base)]=\"upload.status === 'error' || upload.status === 'rejected'\"\n [attr.title]=\"upload.name || (label(upload) | transloco)\"\n >{{ upload.name || (label(upload) | transloco) }}</span\n >\n @if (upload.message) {\n <span class=\"max-w-40 shrink truncate text-[11px] text-(--font-neutral-muted)\" [attr.title]=\"upload.message\">{{ upload.message }}</span>\n }\n\n @switch (upload.status) {\n @case (\"uploading\") {\n <span class=\"shrink-0 text-xs text-(--font-neutral-muted) tabular-nums\">{{ round(upload.percent) }}%</span>\n <!-- A transfer this client does not hold cannot be stopped from here, and nor can it be closed while it goes on. -->\n @if (!upload.remote) {\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.cancelUpload' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.cancelUpload' | transloco\" (click)=\"discard.emit(upload.id)\">\n <x-mark-icon />\n </button>\n }\n }\n @case (\"indexing\") {\n <!-- No percentage here: the server only reports one per job, shown by the bar above. -->\n <activity-dots class=\"shrink-0 text-(--font-neutral-muted)\" />\n }\n @case (\"done\") {\n <span class=\"shrink-0 text-xs whitespace-nowrap text-(--font-success-base)\">{{ \"documentWorkspaces.tracker.complete\" | transloco }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.dismiss' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.dismiss' | transloco\" (click)=\"discard.emit(upload.id)\">\n <x-mark-icon />\n </button>\n }\n @case (\"error\") {\n @if (upload.retryable) {\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.retryUpload' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.retryUpload' | transloco\" (click)=\"retry.emit(upload.id)\">\n <refresh-icon />\n </button>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.dismiss' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.dismiss' | transloco\" (click)=\"discard.emit(upload.id)\">\n <x-mark-icon />\n </button>\n }\n @case (\"rejected\") {\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.tracker.dismiss' | transloco\" [tooltip]=\"'documentWorkspaces.tracker.dismiss' | transloco\" (click)=\"discard.emit(upload.id)\">\n <x-mark-icon />\n </button>\n }\n }\n </li>\n }\n </ul>\n </div>\n `\n})\nexport class UploadTrackerSection {\n readonly status = input.required<UploadStatus>();\n readonly uploads = input.required<readonly TrackedUpload[]>();\n readonly open = input(true);\n /** The phase's overall progress, when the tracker knows better than the mean of the files listed here (those that moved on are not). */\n readonly progress = input<number | undefined>(undefined);\n\n readonly toggle = output<void>();\n /** Cancel an upload in progress, or dismiss a finished row. The tracker tells the two apart. */\n readonly discard = output<string>();\n readonly retry = output<string>();\n\n protected readonly config = computed(() => SECTIONS[this.status()]);\n /** A failure, or a file the server will not index: both are in the error colours. */\n protected readonly failed = computed(() => this.status() === \"error\" || this.status() === \"rejected\");\n\n /** The bar: the phase's overall progress, or else the mean progress of the files in the section. */\n protected readonly average = computed(() => {\n const progress = this.progress();\n if (progress !== undefined) return progress;\n const uploads = this.uploads();\n return uploads.length ? Math.round(uploads.reduce((sum, u) => sum + u.percent, 0) / uploads.length) : 0;\n });\n\n /** The translation key standing for a row with no file name: a job started elsewhere. Only read when `name` is empty. */\n protected label(upload: TrackedUpload): string {\n return upload.remote?.mine ? \"documentWorkspaces.tracker.ownUpload\" : \"documentWorkspaces.tracker.remoteUpload\";\n }\n\n protected round(value: number): number {\n return Math.round(value);\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, DestroyRef, effect, inject, signal, untracked } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n CircleXIcon,\n SyncIcon,\n UploadIcon,\n XMarkIcon,\n TooltipDirective\n} from \"@sinequa/galactik\";\nimport type { TrackedUpload, UploadStatus } from \"../../models/upload.model\";\nimport { UploadTrackerStore, isFailed, isFinished } from \"../../upload/upload-tracker.store\";\nimport { UploadService } from \"../../upload/upload.service\";\nimport { Message, trackerMessage, truncateName, uploadLabel } from \"../../utils/message\";\nimport { FINISHED_UPLOAD_VISIBLE_MS } from \"../../utils/upload-jobs\";\nimport { ActivityDots, ActivitySpinner } from \"../shared/activity-indicators\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\nimport { ConnectionBanner } from \"./connection-banner\";\nimport { UploadTrackerSection } from \"./upload-tracker-section\";\n\n/**\n * - `inline`: a compact header and a line of counters;\n * - `expanded`: the files, section by section, workspace by workspace;\n * - `minimized`: slid down to the corner, only a strip showing; it still reports what happens, and slides back when hovered.\n */\ntype Mode = \"inline\" | \"expanded\" | \"minimized\";\n\ntype Tone = \"normal\" | \"flash\" | \"success\";\n\ninterface TrackedWorkspace {\n id: string;\n name: string;\n}\n\nconst SECTION_ORDER: readonly UploadStatus[] = [\"uploading\", \"indexing\", \"done\", \"error\", \"rejected\"];\n\n// How long what just happened stays on screen, in ms.\nconst FLASH_MS = 3000;\nconst PEEK_MS = 3500;\nconst WORKSPACE_COMPLETE_MS = 3500;\n// How long a panel whose uploads are all over stays: long enough to read the outcome, even of an\n// upload someone else started and the user was not watching.\nconst AUTO_DISMISS_MS = FINISHED_UPLOAD_VISIBLE_MS;\nconst DISMISS_ANIMATION_MS = 320;\nconst HOVER_LEAVE_MS = 250;\n// The text on the strip leaves, then the new one comes: half a transition each.\nconst PEEK_SWAP_MS = 150;\nconst PEEK_REFLOW_MS = 30;\n\n/**\n * The floating panel that follows files from the first byte sent to the end of\n * indexing: what is moving, what is done, what failed and why, and what can be\n * done about it (cancel a transfer, retry a failed one, dismiss a finished one).\n *\n * It only reads `UploadTrackerStore` — the state — and asks `UploadService` to\n * act. It shows itself when files are tracked and, once every file is indexed\n * without a failure, celebrates and dismisses itself.\n *\n * The moments that pass by themselves (a file just finished, a workspace just\n * completed, a connection just dropped) are noticed from changes in the store,\n * so they work the same whether `UploadService` or a demo page drives it.\n */\n@Component({\n selector: \"upload-tracker\",\n imports: [\n TranslocoPipe,\n ActivityDots,\n ActivitySpinner,\n ButtonComponent,\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n CircleXIcon,\n ConnectionBanner,\n SyncIcon,\n TooltipDirective,\n UploadIcon,\n UploadTrackerSection,\n WorkspaceMessage,\n XMarkIcon\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"contents\" },\n template: `\n @if (store.active()) {\n <section\n class=\"group/tracker fixed right-6 bottom-6 z-900 w-[360px] max-w-[calc(100vw-2rem)] overflow-hidden rounded-(--radius-lg) border border-(--stroke-primary-lighter) bg-(--bg-neutral-white) shadow-[0_4px_8px_rgba(0,0,0,0.06),0_16px_32px_rgba(0,0,0,0.1)] transition-transform duration-[400ms] ease-[cubic-bezier(0.34,1.56,0.64,1)] motion-reduce:transition-none data-[state=minimized]:translate-x-[140px] data-[state=minimized]:translate-y-[calc(1.5rem+100%-44px)] data-[state=minimized]:cursor-pointer data-[state=minimized-hover]:translate-x-0 data-[state=minimized-hover]:translate-y-0 data-[state=minimized-hover]:cursor-pointer data-[state=minimized-hover]:duration-300 data-[state=minimized-hover]:ease-out data-minimizing:duration-[180ms] data-minimizing:ease-in data-dismissing:pointer-events-none data-dismissing:translate-y-3 data-dismissing:scale-[0.96] data-dismissing:opacity-0 data-dismissing:transition-[opacity,transform] data-dismissing:duration-300 data-dismissing:ease-[ease]\"\n [attr.data-state]=\"state()\"\n [attr.data-mode]=\"mode()\"\n [attr.data-tone]=\"header().tone\"\n [attr.data-minimizing]=\"minimizing() ? '' : null\"\n [attr.data-dismissing]=\"dismissing() ? '' : null\"\n (mouseenter)=\"onPointerEnter()\"\n (mouseleave)=\"onPointerLeave()\"\n (click)=\"onPanelClick()\"\n (transitionend)=\"onTransitionEnd($event)\">\n <!-- What shows of the panel once it slid down to the corner: a strip that keeps saying what goes on. -->\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[2] flex h-11 items-center gap-2 rounded-t-(--radius-lg) bg-(--bg-primary-lightest) px-4 opacity-0 transition-opacity duration-200 group-data-[state=minimized]/tracker:pointer-events-auto group-data-[state=minimized]/tracker:opacity-100 data-[kind=errors]:bg-(--bg-error-lightest) data-[kind=done]:bg-(--bg-success-lightest)\"\n [attr.data-kind]=\"peekKind()\"\n data-testid=\"peek\">\n @switch (peekTone()) {\n @case (\"success\") {\n <check-icon class=\"size-4 shrink-0 text-(--font-success-base)\" />\n }\n @case (\"error\") {\n <circle-x-icon class=\"size-4 shrink-0 text-(--font-error-base)\" />\n }\n @default {\n <activity-spinner class=\"size-4\" />\n }\n }\n <span\n class=\"min-w-0 flex-1 truncate text-xs text-(--font-neutral-black) transition-[opacity,transform] duration-150 data-[swap=below]:translate-y-[5px] data-[swap=below]:opacity-0 data-[swap=hidden]:-translate-y-[5px] data-[swap=hidden]:opacity-0 data-[tone=error]:font-medium data-[tone=error]:text-(--font-error-base) data-[tone=success]:font-medium data-[tone=success]:text-(--font-success-base)\"\n [attr.data-swap]=\"peekSwap()\"\n [attr.data-tone]=\"peekTone()\"\n aria-live=\"polite\"\n data-testid=\"peek-text\">\n <workspace-message [message]=\"peekShown()\" />\n </span>\n <span class=\"shrink-0 translate-x-1.5 text-xs text-(--font-neutral-muted) opacity-0 transition-[opacity,transform] delay-100 duration-200 group-hover/tracker:translate-x-0 group-hover/tracker:opacity-100\">\n {{ \"documentWorkspaces.tracker.clickToRestore\" | transloco }}\n </span>\n </div>\n\n <header\n class=\"flex items-center gap-3 border-b border-(--stroke-primary-lighter) bg-(--bg-primary-lightest) px-4 py-3 data-[tone=success]:bg-(--bg-success-lightest)\"\n [attr.data-tone]=\"header().tone === 'success' ? 'success' : null\">\n @if (header().tone === \"success\") {\n <check-icon class=\"size-5 shrink-0 text-(--font-success-base)\" />\n } @else {\n <activity-spinner class=\"size-5\" />\n }\n <div class=\"flex min-w-0 flex-1 flex-col gap-px\">\n <span class=\"truncate text-sm font-semibold text-(--font-neutral-black) group-data-[tone=success]/tracker:text-(--font-success-base)\" data-testid=\"name\">\n <workspace-message [message]=\"header().title\" />\n </span>\n <span\n class=\"truncate text-xs text-(--font-neutral-muted) transition-colors duration-200 data-[tone=flash]:font-medium data-[tone=flash]:text-(--font-success-base) data-[tone=success]:font-medium data-[tone=success]:text-(--font-success-base)\"\n [attr.data-tone]=\"header().tone\"\n aria-live=\"polite\"\n data-testid=\"count\">\n <workspace-message [message]=\"header().subtitle\" />\n </span>\n </div>\n <div class=\"relative flex shrink-0 items-center gap-1\">\n <span class=\"pointer-events-none absolute right-0 text-xs whitespace-nowrap text-(--font-primary-base) opacity-0 transition-opacity duration-200 group-data-[state=minimized-hover]/tracker:opacity-100\">\n {{ \"documentWorkspaces.tracker.clickToOpen\" | transloco }}\n </span>\n @if (canRetryAll()) {\n <button\n variant=\"secondary\"\n size=\"sm\"\n class=\"transition-opacity duration-150 group-data-[state=minimized-hover]/tracker:pointer-events-none group-data-[state=minimized-hover]/tracker:opacity-0\"\n (click)=\"retryAll(); $event.stopPropagation()\">{{ \"documentWorkspaces.tracker.retryAll\" | transloco }}</button>\n }\n @if (!store.allDone()) {\n <button\n class=\"transition-opacity duration-150 group-data-[state=minimized-hover]/tracker:pointer-events-none group-data-[state=minimized-hover]/tracker:opacity-0\"\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [attr.aria-expanded]=\"mode() === 'expanded'\"\n [attr.aria-label]=\"(mode() === 'expanded' ? 'documentWorkspaces.common.collapse' : 'documentWorkspaces.common.expand') | transloco\"\n [tooltip]=\"(mode() === 'expanded' ? 'documentWorkspaces.common.collapse' : 'documentWorkspaces.common.expand') | transloco\"\n (click)=\"toggleExpanded(); $event.stopPropagation()\">\n <chevron-down-icon class=\"transition-transform duration-[250ms]\" [class.rotate-180]=\"mode() !== 'expanded'\" />\n </button>\n }\n <button\n class=\"transition-opacity duration-150 group-data-[state=minimized-hover]/tracker:pointer-events-none group-data-[state=minimized-hover]/tracker:opacity-0\"\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"(mode() === 'minimized' || store.settled() ? 'documentWorkspaces.common.close' : 'documentWorkspaces.common.minimize') | transloco\"\n [tooltip]=\"(mode() === 'minimized' || store.settled() ? 'documentWorkspaces.common.close' : 'documentWorkspaces.common.minimize') | transloco\"\n (click)=\"dismiss(); $event.stopPropagation()\">\n <x-mark-icon />\n </button>\n </div>\n </header>\n\n @if (store.connection(); as notice) {\n <connection-banner [notice]=\"notice\" (retry)=\"notice.retry?.()\" />\n }\n\n @if (mode() !== \"expanded\" && !store.allDone() && !completingWorkspace()) {\n <div class=\"flex flex-wrap items-center gap-x-3 gap-y-1 px-4 py-3\" data-testid=\"stats\">\n @for (chip of chips(); track chip.kind) {\n @if ($index > 0) {\n <span class=\"h-3.5 w-px shrink-0 bg-(--stroke-primary-lighter)\" aria-hidden=\"true\"></span>\n }\n <span\n class=\"inline-flex items-center gap-1.5 text-xs whitespace-nowrap text-(--font-neutral-muted) data-[kind=done]:text-(--font-success-base) data-[kind=failed]:text-(--font-error-base)\"\n [attr.data-kind]=\"chip.kind\">\n @switch (chip.kind) {\n @case (\"uploading\") {\n <upload-icon class=\"size-3.5 shrink-0\" />\n }\n @case (\"indexing\") {\n <sync-icon class=\"size-3.5 shrink-0\" />\n }\n @case (\"done\") {\n <check-icon class=\"size-3.5 shrink-0\" />\n }\n @default {\n <circle-x-icon class=\"size-3.5 shrink-0\" />\n }\n }\n <span><workspace-message [message]=\"chip.message\" /></span>\n @if (chip.busy) {\n <activity-dots class=\"-ml-1.5\" />\n }\n </span>\n }\n </div>\n }\n\n @if (mode() === \"expanded\") {\n <div class=\"flex max-h-[60vh] flex-col overflow-y-auto\">\n @if (store.workspaces().length > 1) {\n <div class=\"flex items-center gap-2 border-b border-(--stroke-primary-lighter) px-3 py-2\" data-testid=\"workspace-nav\">\n <button\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"workspaceIndex() === 0\"\n [attr.aria-label]=\"'documentWorkspaces.tracker.previousWorkspace' | transloco\"\n (click)=\"moveWorkspace(-1)\">\n <chevron-left-icon />\n </button>\n <span class=\"flex min-w-0 flex-1 items-center justify-center gap-2 truncate text-xs font-semibold text-(--font-neutral-black)\">\n {{ currentWorkspace()?.name }}\n <span class=\"shrink-0 font-normal text-(--font-neutral-muted)\">{{ currentIndex() + 1 }}/{{ store.workspaces().length }}</span>\n </span>\n <button\n variant=\"tertiary\" scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"currentIndex() >= store.workspaces().length - 1\"\n [attr.aria-label]=\"'documentWorkspaces.tracker.nextWorkspace' | transloco\"\n (click)=\"moveWorkspace(1)\">\n <chevron-right-icon />\n </button>\n </div>\n }\n @for (section of sections(); track section.status) {\n <upload-tracker-section\n class=\"border-(--stroke-primary-lighter) not-first:border-t\"\n [status]=\"section.status\"\n [uploads]=\"section.uploads\"\n [progress]=\"section.progress\"\n [open]=\"isOpen(section.status)\"\n (toggle)=\"toggleSection(section.status)\"\n (discard)=\"discard($event)\"\n (retry)=\"retry($event)\" />\n }\n </div>\n }\n </section>\n }\n `\n})\nexport class UploadTracker {\n protected readonly store = inject(UploadTrackerStore);\n private readonly service = inject(UploadService);\n\n protected readonly mode = signal<Mode>(\"inline\");\n protected readonly workspaceIndex = signal(0);\n /** Sections the user folded, keyed `<workspaceId>:<status>`; open by default. */\n private readonly folded = signal<ReadonlySet<string>>(new Set());\n\n /** What has just happened, shown for a moment where the file count usually is. */\n private readonly flash = signal<Message | null>(null);\n /** The same for a minimized panel, which has nowhere else to say it. */\n private readonly peek = signal<{ message: Message; tone: \"success\" | \"error\" } | null>(null);\n protected readonly completingWorkspace = signal<TrackedWorkspace | null>(null);\n protected readonly dismissing = signal(false);\n /** The pointer is over a minimized panel: it slides back up, to be clicked open. */\n private readonly hover = signal(false);\n /** The panel is on its way down to the corner: quicker than the way back. */\n protected readonly minimizing = signal(false);\n /** What the strip of a minimized panel shows, which is not always what it should: a change swaps it. */\n protected readonly peekShown = signal<Message>({ text: \"\" });\n protected readonly peekTone = signal<\"success\" | \"error\" | null>(null);\n protected readonly peekSwap = signal<\"shown\" | \"hidden\" | \"below\">(\"shown\");\n private peekKey = \"\";\n private hoverTimer: ReturnType<typeof setTimeout> | null = null;\n\n private readonly timers = new Set<ReturnType<typeof setTimeout>>();\n private flashTimer: ReturnType<typeof setTimeout> | null = null;\n private peekTimer: ReturnType<typeof setTimeout> | null = null;\n private peekTimer2: ReturnType<typeof setTimeout> | null = null;\n private dismissTimer: ReturnType<typeof setTimeout> | null = null;\n private completeTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** Status of each file as of the last change, to tell what just finished from what already had. */\n private previous = new Map<string, UploadStatus>();\n private previousPhase: string | null = null;\n\n // ── what to show ──\n\n protected readonly currentIndex = computed(() => Math.min(this.workspaceIndex(), Math.max(0, this.store.workspaces().length - 1)));\n protected readonly currentWorkspace = computed<TrackedWorkspace | null>(() => this.store.workspaces()[this.currentIndex()] ?? null);\n\n protected readonly sections = computed(() => {\n const workspace = this.currentWorkspace();\n if (!workspace) return [];\n const uploads = this.store.uploads().filter(u => u.workspaceId === workspace.id);\n const progress = this.store.progress()[workspace.id];\n return SECTION_ORDER.map(status => ({\n status,\n uploads: uploads.filter(u => u.status === status),\n // Only the two phases that move have a bar; the others never read it.\n progress: status === \"uploading\" || status === \"indexing\" ? progress?.[status] : undefined\n })).filter(section => section.uploads.length);\n });\n\n protected readonly header = computed<{ title: Message; subtitle: Message; tone: Tone }>(() => {\n const workspaces = this.store.workspaces();\n const completing = this.completingWorkspace();\n if (completing) return { title: { text: truncateName(completing.name, 28) }, subtitle: trackerMessage(\"complete\"), tone: \"success\" };\n\n const title: Message = workspaces.length === 1 ? { text: workspaces[0].name } : trackerMessage(\"workspaceCount\", { count: workspaces.length });\n // Everything indexed and nothing failed: the header carries the whole success message.\n if (this.store.allDone()) return { title, subtitle: trackerMessage(\"indexedFileCount\", { count: this.store.indexedCount() }), tone: \"success\" };\n const flash = this.flash();\n if (flash && this.mode() === \"inline\") return { title, subtitle: flash, tone: \"flash\" };\n return { title, subtitle: trackerMessage(\"fileCount\", { count: this.store.uploads().length }), tone: \"normal\" };\n });\n\n /** The counters of the compact view: what moves, what finished, what failed. */\n protected readonly chips = computed(() => {\n const groups = this.store.byStatus();\n const failed = this.store.failed();\n const severalWorkspaces = this.store.workspaces().length > 1;\n // One file is named, several are counted.\n const label = (uploads: readonly TrackedUpload[], key: string, max: number): Message =>\n uploads.length === 1 ? uploadLabel(uploads[0], max) : trackerMessage(key, { count: uploads.length });\n\n const chips: { kind: string; message: Message; busy: boolean }[] = [];\n if (groups.uploading.length) chips.push({ kind: \"uploading\", busy: true, message: trackerMessage(\"uploadingCount\", { count: groups.uploading.length }) });\n if (groups.indexing.length) chips.push({ kind: \"indexing\", busy: true, message: trackerMessage(\"indexingCount\", { count: groups.indexing.length }) });\n // With several workspaces, finished files are reported per workspace as each completes.\n if (groups.done.length && !severalWorkspaces) chips.push({ kind: \"done\", busy: false, message: label(groups.done, \"completedCount\", 26) });\n if (failed.length) chips.push({ kind: \"failed\", busy: false, message: label(failed, \"failedCount\", 26) });\n return chips;\n });\n\n /** What the minimized pill says: a notification while there is one, otherwise a summary. */\n protected readonly pillMessage = computed<Message>(() => {\n const peek = this.peek();\n if (peek) return peek.message;\n const failed = this.store.failed();\n if (failed.length) return failed.length === 1 ? uploadLabel(failed[0], 22) : trackerMessage(\"errorCount\", { count: failed.length });\n const { uploading, indexing, done } = this.store.byStatus();\n const active = uploading.length + indexing.length;\n if (done.length && !active) return done.length === 1 ? uploadLabel(done[0], 22) : trackerMessage(\"indexedCount\", { count: done.length });\n return trackerMessage(\"fileCount\", { count: active });\n });\n\n protected readonly state = computed(() => (this.mode() !== \"minimized\" ? \"normal\" : this.hover() ? \"minimized-hover\" : \"minimized\"));\n\n /** The strip's own colour: the failure, or the success, that stands. */\n protected readonly peekKind = computed(() => (this.store.allDone() ? \"done\" : this.store.failed().length ? \"errors\" : null));\n\n protected readonly canRetryAll = computed(() => this.store.uploads().some(u => u.status === \"error\" && u.retryable));\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.timers.forEach(clearTimeout));\n\n // React to what changed in the store: new files, files that just finished or failed.\n effect(() => {\n const uploads = this.store.uploads();\n untracked(() => this.onUploadsChanged(uploads));\n });\n\n // A workspace whose files are all indexed, while others are still going.\n effect(() => {\n const workspaces = this.store.workspaces();\n const uploads = this.store.uploads();\n const allDone = this.store.allDone();\n untracked(() => {\n if (allDone || workspaces.length < 2 || this.completingWorkspace()) return;\n const complete = workspaces.find(w => {\n const own = uploads.filter(u => u.workspaceId === w.id);\n return own.length > 0 && own.every(isFinished) && !own.some(isFailed);\n });\n if (complete) this.completeWorkspace(complete);\n });\n });\n\n // Everything is over: hold the outcome for a while, then go away — unless a failure of the\n // user's own is there to be read, which stays until they close it. A new upload cancels\n // this (nothing is settled any more), and the delay starts afresh when it ends.\n effect(() => {\n const leaves = this.store.settled() && !this.store.authorFailed();\n untracked(() => (leaves ? this.startAutoDismiss() : this.cancelAutoDismiss()));\n });\n\n // The strip of a minimized panel says what is going on; when that changes, the text is swapped, not cut.\n effect(() => {\n const message = this.pillMessage();\n const tone = this.peek()?.tone ?? null;\n untracked(() => this.syncPeek(message, tone));\n });\n\n // A minimized panel has no banner: a change of connection phase goes to the strip.\n effect(() => {\n const phase = this.store.connection()?.phase ?? null;\n untracked(() => {\n if (phase && phase !== this.previousPhase) this.notify(trackerMessage(phase === \"signed-out\" ? \"signedOut\" : \"connectionLost\"), \"error\");\n this.previousPhase = phase;\n });\n });\n }\n\n // ── what the user can do ──\n\n protected restore(): void {\n this.clearHover();\n this.mode.set(\"inline\");\n }\n\n protected onPanelClick(): void {\n if (this.mode() === \"minimized\") this.restore();\n }\n\n protected onPointerEnter(): void {\n if (this.mode() !== \"minimized\") return;\n if (this.hoverTimer) clearTimeout(this.hoverTimer);\n this.hoverTimer = null;\n this.hover.set(true);\n }\n\n protected onPointerLeave(): void {\n if (this.mode() !== \"minimized\") return;\n // Not at once: a pointer that strays off the edge while the panel slides should not make it flap.\n this.hoverTimer = this.replaceTimer(this.hoverTimer, HOVER_LEAVE_MS, () => {\n this.hoverTimer = null;\n this.hover.set(false);\n });\n }\n\n protected onTransitionEnd(event: TransitionEvent): void {\n if (event.target === event.currentTarget && event.propertyName === \"transform\") this.minimizing.set(false);\n }\n\n private clearHover(): void {\n if (this.hoverTimer) clearTimeout(this.hoverTimer);\n this.hoverTimer = null;\n this.hover.set(false);\n }\n\n protected toggleExpanded(): void {\n if (this.mode() === \"minimized\") {\n this.restore();\n return;\n }\n this.mode.update(mode => (mode === \"expanded\" ? \"inline\" : \"expanded\"));\n }\n\n /** Minimizes while there is work going on; once everything is over (failures included), closes for good. */\n protected dismiss(): void {\n if (this.store.settled() || this.mode() === \"minimized\") {\n this.reset();\n } else {\n this.clearHover();\n this.minimizing.set(true);\n this.mode.set(\"minimized\");\n }\n }\n\n protected moveWorkspace(step: -1 | 1): void {\n this.workspaceIndex.set(Math.max(0, Math.min(this.currentIndex() + step, this.store.workspaces().length - 1)));\n }\n\n protected isOpen(status: UploadStatus): boolean {\n return !this.folded().has(this.sectionKey(status));\n }\n\n protected toggleSection(status: UploadStatus): void {\n const key = this.sectionKey(status);\n this.folded.update(folded => {\n const next = new Set(folded);\n if (!next.delete(key)) next.add(key);\n return next;\n });\n }\n\n /**\n * Cancels a transfer in progress, or drops a row that is over. Only the\n * service knows the transfers it can still act on; a row it does not know\n * (finished, or failed on the server) is simply taken off the list.\n */\n protected async discard(id: string): Promise<void> {\n await this.service.cancel(id);\n this.store.remove(id);\n }\n\n protected retry(id: string): void {\n this.service.retry(id);\n }\n\n protected retryAll(): void {\n this.service.resumeStalled();\n }\n\n // ── moments that pass by themselves ──\n\n private onUploadsChanged(uploads: readonly TrackedUpload[]): void {\n const before = this.previous;\n this.previous = new Map(uploads.map(u => [u.id, u.status]));\n\n const added = uploads.filter(u => !before.has(u.id));\n if (added.length) {\n // New files: whatever was on screen makes way for them.\n this.cancelAutoDismiss();\n if (this.mode() !== \"inline\") this.mode.set(\"inline\");\n this.workspaceIndex.set(Math.max(0, this.store.workspaces().length - 1));\n }\n\n const finished = uploads.filter(u => before.has(u.id) && before.get(u.id) !== u.status);\n const done = finished.filter(u => u.status === \"done\");\n const failed = finished.filter(isFailed);\n\n if (done.length) {\n const last = done[done.length - 1];\n const message = uploadLabel(last, this.mode() === \"minimized\" ? 22 : 30);\n if (this.mode() === \"minimized\") this.notify(message, \"success\");\n else if (this.mode() === \"inline\" && !this.store.allDone()) this.showFlash(message);\n }\n if (failed.length && this.mode() === \"minimized\") {\n this.notify(uploadLabel(failed[failed.length - 1], 22), \"error\");\n }\n }\n\n /**\n * Puts `message` on the strip: the old text leaves upward, the new one comes up from below.\n * The first text is simply there, and what is already shown is left alone.\n */\n private syncPeek(message: Message, tone: \"success\" | \"error\" | null): void {\n const key = JSON.stringify([message, tone]);\n if (key === this.peekKey) return;\n const first = this.peekKey === \"\";\n this.peekKey = key;\n if (first) {\n this.peekShown.set(message);\n this.peekTone.set(tone);\n return;\n }\n this.peekSwap.set(\"hidden\");\n this.peekTimer2 = this.replaceTimer(this.peekTimer2, PEEK_SWAP_MS, () => {\n this.peekShown.set(message);\n this.peekTone.set(tone);\n this.peekSwap.set(\"below\");\n // A beat on the \"below\" position, so that the move up is seen.\n this.peekTimer2 = this.replaceTimer(this.peekTimer2, PEEK_REFLOW_MS, () => this.peekSwap.set(\"shown\"));\n });\n }\n\n private showFlash(message: Message): void {\n this.flash.set(message);\n this.flashTimer = this.replaceTimer(this.flashTimer, FLASH_MS, () => this.flash.set(null));\n }\n\n private notify(message: Message, tone: \"success\" | \"error\"): void {\n if (this.mode() !== \"minimized\") return;\n this.peek.set({ message, tone });\n this.peekTimer = this.replaceTimer(this.peekTimer, PEEK_MS, () => this.peek.set(null));\n }\n\n private completeWorkspace(workspace: TrackedWorkspace): void {\n if (this.mode() === \"minimized\") {\n this.notify(trackerMessage(\"workspaceIndexed\", { name: truncateName(workspace.name, 18) }), \"success\");\n this.store.removeWorkspace(workspace.id);\n return;\n }\n // Put the header in charge, so the success is the focus, then clear the workspace away.\n if (this.mode() === \"expanded\") this.mode.set(\"inline\");\n this.completingWorkspace.set(workspace);\n this.completeTimer = this.replaceTimer(this.completeTimer, WORKSPACE_COMPLETE_MS, () => {\n this.store.removeWorkspace(workspace.id);\n this.completingWorkspace.set(null);\n });\n }\n\n private startAutoDismiss(): void {\n if (this.dismissTimer) return;\n // The success message is in the header: make sure it can be seen. Failures are in the sections: leave those open.\n if (this.store.allDone()) this.mode.set(\"inline\");\n this.dismissTimer = this.replaceTimer(null, AUTO_DISMISS_MS, () => {\n this.dismissTimer = null;\n this.dismissing.set(true);\n this.replaceTimer(null, DISMISS_ANIMATION_MS, () => this.reset());\n });\n }\n\n private cancelAutoDismiss(): void {\n if (this.dismissTimer) clearTimeout(this.dismissTimer);\n this.dismissTimer = null;\n this.dismissing.set(false);\n }\n\n private reset(): void {\n this.cancelAutoDismiss();\n for (const timer of [this.flashTimer, this.peekTimer, this.completeTimer]) if (timer) clearTimeout(timer);\n this.flash.set(null);\n this.peek.set(null);\n this.completingWorkspace.set(null);\n this.folded.set(new Set());\n this.clearHover();\n this.minimizing.set(false);\n this.mode.set(\"inline\");\n this.workspaceIndex.set(0);\n this.store.reset();\n }\n\n private sectionKey(status: UploadStatus): string {\n return `${this.currentWorkspace()?.id}:${status}`;\n }\n\n /** Replaces a pending timer with a new one, all cleared together when the panel is destroyed. */\n private replaceTimer(current: ReturnType<typeof setTimeout> | null, ms: number, action: () => void): ReturnType<typeof setTimeout> {\n if (current) {\n clearTimeout(current);\n this.timers.delete(current);\n }\n const timer = setTimeout(() => {\n this.timers.delete(timer);\n action();\n }, ms);\n this.timers.add(timer);\n return timer;\n }\n}\n","import { assertInInjectionContext, DestroyRef, inject } from \"@angular/core\";\nimport { PollResult, StatusPoller } from \"../upload/status-poller\";\n\nexport interface LiveRefreshOptions {\n /** Delay between automatic refreshes while something keeps changing (default 1500ms, see {@link StatusPoller}). */\n intervalMs?: number;\n /** Ceiling the idle backoff climbs to when nothing changes between polls. */\n maxIntervalMs?: number;\n}\n\n/**\n * Keeps a view fresh while it stays open, without the user ever asking: `tick`\n * re-fetches whatever the view shows and reports whether the result differs\n * from before — the loop speeds back up on a change and backs off while idle,\n * exactly like {@link StatusPoller} already does for upload status.\n *\n * Paused while the browser reports no network (resumed the moment it is back),\n * and fully stopped once the injecting component is destroyed. The very first\n * scheduled tick is a no-op: whatever calls `start()` has normally just\n * fetched on its own, so re-fetching the same thing instantly would be\n * redundant — the first real poll happens one `intervalMs` later.\n *\n * Must be called from an injection context.\n */\nexport function injectLiveRefresh(tick: (signal: AbortSignal) => Promise<boolean>, options: LiveRefreshOptions = {}): { start(): void; stop(): void } {\n assertInInjectionContext(injectLiveRefresh);\n\n let shouldRun = false;\n let first = true;\n const poller = new StatusPoller({\n intervalMs: options.intervalMs,\n maxIntervalMs: options.maxIntervalMs,\n tick: async (signal): Promise<PollResult> => {\n if (first) {\n first = false;\n return { pending: true, changed: true };\n }\n return { pending: true, changed: await tick(signal) };\n }\n });\n\n if (typeof window !== \"undefined\") {\n const resume = () => {\n if (shouldRun) poller.start();\n };\n const pause = () => poller.stop();\n window.addEventListener(\"online\", resume);\n window.addEventListener(\"offline\", pause);\n inject(DestroyRef).onDestroy(() => {\n window.removeEventListener(\"online\", resume);\n window.removeEventListener(\"offline\", pause);\n });\n }\n\n inject(DestroyRef).onDestroy(() => poller.stop());\n\n return {\n start(): void {\n shouldRun = true;\n if (typeof navigator === \"undefined\" || navigator.onLine) poller.start();\n },\n stop(): void {\n shouldRun = false;\n poller.stop();\n }\n };\n}\n","import { ChangeDetectionStrategy, Component, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckboxComponent, FileIcon, TagComponent, TrashIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { HighlightedText } from \"../shared/highlighted-text\";\nimport { WorkspaceIcon } from \"../shared/workspace-icon\";\nimport { WorkspaceRoleTag } from \"../shared/workspace-role-tag\";\n\n/**\n * The columns of the list view — the header and every row share it.\n * Written out in full: Tailwind only generates the classes it can read as literals.\n */\nexport const WORKSPACE_ROW_COLUMNS =\n \"grid grid-cols-[minmax(0,1fr)_auto_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_auto_2.5rem] @xl:grid-cols-[minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @xl:data-selecting:grid-cols-[2rem_minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem]\";\n\n/** A workspace in the list view. Same contract as {@link WorkspaceCard}. */\n@Component({\n selector: \"workspace-row\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n CheckboxComponent,\n FileIcon,\n HighlightedText,\n TagComponent,\n TooltipDirective,\n TrashIcon,\n WorkspaceIcon,\n WorkspaceRoleTag,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n role: \"row\",\n class: `${WORKSPACE_ROW_COLUMNS} group border-(--stroke-primary-lighter) bg-(--bg-neutral-white) cursor-pointer rounded-(--radius-md) border py-3 transition-shadow hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)] data-selected:border-(--stroke-primary-base) data-selected:bg-(--bg-primary-lightest) data-locked:cursor-default`,\n \"[attr.data-role]\": \"workspace().currentUserRole\",\n \"[attr.data-selecting]\": \"selecting() ? '' : null\",\n \"[attr.data-selected]\": \"selected() ? '' : null\",\n \"[attr.data-locked]\": \"selecting() && !selectable() ? '' : null\",\n \"(click)\": \"onRowClick($event)\"\n },\n template: `\n @if (selecting()) {\n <div role=\"cell\">\n <checkbox\n [checked]=\"selected()\"\n [disabled]=\"!selectable()\"\n [aria-label]=\"workspace().name\"\n (checkedChange)=\"selectedChange.emit($event)\" />\n </div>\n }\n\n <div role=\"cell\" class=\"flex min-w-0 items-center gap-3\">\n <workspace-icon size=\"md\" [color]=\"workspace().color\" />\n <div class=\"flex min-w-0 flex-col gap-px\">\n <button variant=\"none\" type=\"button\" class=\"block max-w-full truncate text-left text-sm font-semibold text-(--font-neutral-black)\" (click)=\"activate()\">\n <highlighted-text [text]=\"workspace().name\" [query]=\"query()\" />\n </button>\n <span class=\"truncate text-xs text-(--font-primary-muted)\">{{ workspace().description }}</span>\n </div>\n </div>\n\n <div role=\"cell\" class=\"flex items-center gap-1 text-xs text-(--font-primary-base)\">\n <file-icon class=\"size-3 text-(--font-primary-muted)\" />\n {{ workspace().documentCount }}\n </div>\n\n <div role=\"cell\"><workspace-role-tag [role]=\"workspace().currentUserRole\" /></div>\n\n <!-- The API has no update date for a workspace: the column is kept, as in the web element, and says so. -->\n <div role=\"cell\" class=\"text-xs text-(--font-primary-muted) hidden @xl:block\" data-testid=\"updated\">—</div>\n\n <div role=\"cell\" class=\"flex min-w-0 flex-nowrap items-center gap-1 overflow-hidden hidden @3xl:flex\">\n @for (tag of workspace().tags; track tag) {\n <tag scheme=\"grey\" size=\"xs\">{{ tag }}</tag>\n }\n </div>\n\n <div role=\"cell\" class=\"flex justify-end\">\n @if (!selecting()) {\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n class=\"text-(--font-error-muted) opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-(--bg-error-lightest) hover:text-(--font-error-base) max-md:opacity-100\"\n [iconOnly]=\"true\"\n [writerOnly]=\"workspace().currentUserRole\"\n [attr.aria-label]=\"'documentWorkspaces.list.deleteWorkspace' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.delete' | transloco\"\n (click)=\"deleteRequested.emit()\">\n <trash-icon />\n </button>\n }\n </div>\n `\n})\nexport class WorkspaceRow {\n readonly workspace = input.required<Workspace>();\n readonly query = input(\"\");\n readonly selecting = input(false);\n readonly selected = input(false);\n readonly selectable = input(true);\n\n readonly opened = output<void>();\n readonly deleteRequested = output<void>();\n readonly selectedChange = output<boolean>();\n\n protected activate(): void {\n if (this.selecting()) {\n if (this.selectable()) this.selectedChange.emit(!this.selected());\n } else {\n this.opened.emit();\n }\n }\n\n protected onRowClick(event: MouseEvent): void {\n if ((event.target as HTMLElement).closest(\"button, input, label\")) return;\n this.activate();\n }\n}\n","import { ChangeDetectionStrategy, Component, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckboxComponent, FileIcon, TagComponent, TrashIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { HighlightedText } from \"../shared/highlighted-text\";\nimport { WorkspaceIcon } from \"../shared/workspace-icon\";\nimport { WorkspaceRoleTag } from \"../shared/workspace-role-tag\";\n\n/**\n * A workspace in the grid.\n *\n * It only reports what the user asked for — open it, delete it, tick it — and\n * leaves doing it to the list. Opening is on the title, a real button, so the\n * keyboard reaches it; the whole card is clickable as a convenience for the pointer.\n */\n@Component({\n selector: \"workspace-card\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n CheckboxComponent,\n FileIcon,\n HighlightedText,\n TagComponent,\n TooltipDirective,\n TrashIcon,\n WorkspaceIcon,\n WorkspaceRoleTag,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class:\n \"group border-(--stroke-primary-lighter) bg-(--bg-neutral-white) flex cursor-pointer flex-col rounded-(--radius-md) border p-4 transition-shadow hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)] data-selected:border-(--stroke-primary-base) data-selected:bg-(--bg-primary-lightest) data-locked:cursor-default\",\n \"[attr.data-role]\": \"workspace().currentUserRole\",\n \"[attr.data-selected]\": \"selected() ? '' : null\",\n // In selection mode a workspace that cannot be selected is shown as such instead of silently ignoring the click.\n \"[attr.data-locked]\": \"selecting() && !selectable() ? '' : null\",\n \"(click)\": \"onCardClick($event)\"\n },\n template: `\n <div class=\"mb-3 flex items-start gap-3\">\n <workspace-icon [color]=\"workspace().color\" />\n <h3 class=\"min-w-0 flex-1 pt-2 text-base leading-[22px] font-semibold text-(--font-neutral-black)\">\n <button variant=\"none\" type=\"button\" class=\"block max-w-full truncate text-left\" (click)=\"activate()\">\n <highlighted-text [text]=\"workspace().name\" [query]=\"query()\" />\n </button>\n </h3>\n @if (selecting()) {\n <checkbox\n [checked]=\"selected()\"\n [disabled]=\"!selectable()\"\n [aria-label]=\"workspace().name\"\n (checkedChange)=\"selectedChange.emit($event)\" />\n } @else {\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n class=\"text-(--font-error-muted) opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-(--bg-error-lightest) hover:text-(--font-error-base) max-md:opacity-100\"\n [iconOnly]=\"true\"\n [writerOnly]=\"workspace().currentUserRole\"\n [attr.aria-label]=\"'documentWorkspaces.list.deleteWorkspace' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.delete' | transloco\"\n (click)=\"deleteRequested.emit()\">\n <trash-icon />\n </button>\n }\n </div>\n\n @if (workspace().tags.length) {\n <div class=\"mb-3 flex flex-wrap items-center gap-1\">\n @for (tag of workspace().tags; track tag) {\n <tag scheme=\"grey\" size=\"xs\">{{ tag }}</tag>\n }\n </div>\n }\n\n <p class=\"mb-3 line-clamp-4 flex-1 text-sm leading-[18px] text-(--font-primary-base)\">{{ workspace().description }}</p>\n\n <div class=\"mb-3 h-px bg-(--stroke-primary-lighter)\"></div>\n <div class=\"flex items-center justify-between gap-2\">\n <workspace-role-tag [role]=\"workspace().currentUserRole\" />\n <span class=\"ml-auto flex shrink-0 items-center gap-1 text-xs text-(--font-primary-muted)\">\n <file-icon class=\"size-3\" />\n {{ workspace().documentCount }}\n </span>\n </div>\n `\n})\nexport class WorkspaceCard {\n readonly workspace = input.required<Workspace>();\n /** The search box's text, to mark in the name. */\n readonly query = input(\"\");\n /** The list is in selection mode: checkboxes show, and a click ticks instead of opening. */\n readonly selecting = input(false);\n readonly selected = input(false);\n /** Whether this workspace may be ticked — not one shared read-only. */\n readonly selectable = input(true);\n\n readonly opened = output<void>();\n readonly deleteRequested = output<void>();\n readonly selectedChange = output<boolean>();\n\n /** Opens the workspace — or, in selection mode, ticks it. */\n protected activate(): void {\n if (this.selecting()) {\n if (this.selectable()) this.selectedChange.emit(!this.selected());\n } else {\n this.opened.emit();\n }\n }\n\n protected onCardClick(event: MouseEvent): void {\n // A button or the checkbox has handled it already.\n if ((event.target as HTMLElement).closest(\"button, input, label\")) return;\n this.activate();\n }\n}\n","import { ChangeDetectionStrategy, Component, inject, Injector, output, signal } from \"@angular/core\";\nimport { form, FormField } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe, TranslocoService } from \"@jsverse/transloco\";\nimport {\n BadgeComponent,\n ButtonComponent,\n CheckboxComponent,\n GridIcon,\n ListIcon,\n PlusIcon,\n SearchInputComponent,\n TabComponent,\n TabListComponent,\n TabsComponent,\n TrashIcon,\n UserIcon,\n UserRoundIcon,\n TooltipDirective\n} from \"@sinequa/galactik\";\nimport { injectLiveRefresh } from \"../../composables/inject-live-refresh\";\nimport { injectSelection } from \"../../composables/inject-selection\";\nimport { injectWorkspaceFilter, WorkspaceTab } from \"../../composables/inject-workspace-filter\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { UploadService } from \"../../upload/upload.service\";\nimport { workspaceErrorMessage } from \"../../utils/error-message\";\nimport { Message } from \"../../utils/message\";\nimport { canWrite } from \"../../utils/workspace-role\";\nimport { ConfirmAction, ConfirmDialogConfig } from \"../shared/confirm.dialog\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\nimport { WORKSPACE_ROW_COLUMNS, WorkspaceRow } from \"./workspace-row\";\nimport { WorkspaceCard } from \"./workspace-card\";\n\ntype ViewMode = \"grid\" | \"list\";\n\n/** The tabs, in order: the translation key of each one's label, and the badge count it carries. */\nconst TABS: readonly { tab: WorkspaceTab; labelKey: string }[] = [\n { tab: \"all\", labelKey: \"documentWorkspaces.list.tabAll\" },\n { tab: \"editor\", labelKey: \"documentWorkspaces.list.tabEditor\" },\n { tab: \"reader\", labelKey: \"documentWorkspaces.list.tabReader\" }\n];\n\n/**\n * The home of the workspaces view: every workspace you can reach, as a grid or a\n * list, filtered by tab and by search, with multi-selection and deletion.\n *\n * It reads `WorkspacesStore` and does not know how a workspace is opened or\n * created — it says so (`opened`, `createRequested`) and whoever hosts it decides.\n *\n * Each time it is shown it refreshes: workspaces created by others since the last\n * time appear, and the document counts match the server. The list already on\n * screen stays until the answer lands, and a failure leaves it untouched behind\n * an inline error. While it stays shown, it keeps polling in the background\n * (`injectLiveRefresh`) so a workspace created by someone else — one the current\n * user only reads, say — appears on its own, with no action needed.\n */\n@Component({\n selector: \"workspace-list\",\n imports: [\n TranslocoPipe,\n FormField,\n BadgeComponent,\n ButtonComponent,\n CheckboxComponent,\n GridIcon,\n ListIcon,\n PlusIcon,\n SearchInputComponent,\n TabComponent,\n TabListComponent,\n TabsComponent,\n TooltipDirective,\n TrashIcon,\n UserIcon,\n UserRoundIcon,\n WorkspaceCard,\n WorkspaceMessage,\n WorkspaceRow\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"@container flex h-full min-h-0 flex-col p-3 pb-2 md:p-8 md:pb-4\" },\n template: `\n <div class=\"mb-1 flex items-center justify-between gap-4\">\n <h1 class=\"text-2xl leading-[31px] font-bold text-(--font-neutral-black)\">{{ \"documentWorkspaces.list.title\" | transloco }}</h1>\n <button (click)=\"createRequested.emit()\">\n <plus-icon />\n {{ \"documentWorkspaces.list.addWorkspace\" | transloco }}\n </button>\n </div>\n <p class=\"mb-6 text-base text-(--font-primary-base)\">{{ \"documentWorkspaces.list.subtitle\" | transloco }}</p>\n\n <Tabs class=\"mb-3 max-w-full\">\n <TabList\n variant=\"inner\"\n class=\"w-fit max-w-full\"\n selectionMode=\"explicit\"\n [softDisabled]=\"false\"\n [attr.aria-label]=\"'documentWorkspaces.list.title' | transloco\"\n [selectedTab]=\"filter.tab()\"\n (selectedTabChange)=\"onTabChange($event)\">\n @for (item of tabs; track item.tab) {\n <Tab [value]=\"item.tab\" variant=\"inner\">\n @switch (item.tab) {\n @case (\"editor\") {\n <user-icon class=\"shrink-0\" />\n }\n @case (\"reader\") {\n <user-round-icon class=\"shrink-0\" />\n }\n }\n <span>{{ item.labelKey | transloco }}</span>\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"shrink-0\" [attr.data-testid]=\"'badge-' + item.tab\">{{ filter.counts()[item.tab] }}</Badge>\n </Tab>\n }\n </TabList>\n </Tabs>\n\n <div class=\"mb-6 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <SearchInput\n role=\"search\"\n class=\"min-w-32 flex-[0_1_15rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <span class=\"text-sm text-(--font-primary-base)\" aria-live=\"polite\">{{ \"documentWorkspaces.list.selectedCount\" | transloco: { count: selection.count() } }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <trash-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the workspaces scroll: the title, the tabs and the toolbar stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-sm text-(--font-primary-muted)\">{{ \"documentWorkspaces.list.loading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.list.loadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (!filter.filtered().length) {\n <p class=\"py-8 text-center text-sm text-(--font-primary-muted)\" data-testid=\"empty\">\n {{ (store.workspaces().length ? \"documentWorkspaces.list.noMatch\" : \"documentWorkspaces.list.empty\") | transloco }}\n </p>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] content-start gap-4 pb-8\">\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-card\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"${WORKSPACE_ROW_COLUMNS} py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <checkbox\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (checkedChange)=\"selection.toggleAll()\" />\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnName\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnContent\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnOwner\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @xl:block\">{{ \"documentWorkspaces.list.columnDateUpdated\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @3xl:block\">{{ \"documentWorkspaces.list.columnTags\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-row\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n }\n\n @if (store.hasMore() || store.truncated()) {\n <div class=\"flex flex-col items-center gap-2 pb-8\" data-testid=\"load-more\">\n <p class=\"text-sm text-(--font-primary-muted)\">\n {{ \"documentWorkspaces.list.partial\" | transloco: { shown: store.workspaces().length, total: store.pagination().total } }}\n </p>\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.loadMoreError\" | transloco: { error: message } }}</p>\n }\n @if (store.hasMore()) {\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.list.loadMore\" | transloco }}\n </button>\n }\n </div>\n }\n }\n }\n </div>\n `\n})\nexport class WorkspaceList {\n /** A workspace was chosen for opening. */\n readonly opened = output<Workspace>();\n /** The user wants a new workspace. Creating one is the host's dialog. */\n readonly createRequested = output<void>();\n\n protected readonly store = inject(WorkspacesStore);\n private readonly uploads = inject(UploadService);\n private readonly injector = inject(Injector);\n private readonly transloco = inject(TranslocoService);\n\n protected readonly tabs = TABS;\n protected readonly viewMode = signal<ViewMode>(\"grid\");\n protected readonly status = signal<\"loading\" | \"ready\" | \"error\">(\"loading\");\n protected readonly loadError = signal<Message | null>(null);\n protected readonly refreshError = signal<string | null>(null);\n protected readonly loadingMore = signal(false);\n protected readonly loadMoreError = signal<string | null>(null);\n\n protected readonly filter = injectWorkspaceFilter(this.store.workspaces);\n /** The search box, bound to the filter's own query signal. */\n protected readonly searchForm = form(this.filter.query);\n // A workspace shared read-only cannot be deleted: it is not offered for selection either, so the\n // count, \"select all\" and the Delete button never include one.\n protected readonly selection = injectSelection({\n items: this.filter.filtered,\n key: (workspace: Workspace) => workspace.id,\n selectable: workspace => canWrite(workspace.currentUserRole)\n });\n\n // Background polling, started once the list has something to compare against:\n // each tick re-reloads and compares the full result to what was cached before\n // it, not just the set of ids — a workspace renamed, re-tagged or whose access\n // changed keeps the same id, and would otherwise look unchanged forever, which\n // would both hide the edit and widen the backoff on ticks that are not actually idle.\n private readonly liveRefresh = injectLiveRefresh(\n async () => {\n const before = JSON.stringify(this.store.workspaces());\n await this.store.reload();\n const changed = JSON.stringify(this.store.workspaces()) !== before;\n if (changed) this.selection.exit(); // the ticks belonged to the list that was there before\n // An upload someone else is running on a workspace listed here: only the status API tells.\n // It stops by itself after a tick when nothing is moving.\n this.uploads.startPolling();\n return changed;\n },\n { intervalMs: 20_000, maxIntervalMs: 120_000 }\n );\n\n constructor() {\n this.uploads.startPolling();\n void this.load();\n }\n\n /** Shows what is cached at once and refreshes behind it, or loads for the first time. */\n protected async load(): Promise<void> {\n this.loadError.set(null);\n if (this.store.loaded()) {\n this.status.set(\"ready\");\n await this.refresh();\n this.liveRefresh.start();\n return;\n }\n\n this.status.set(\"loading\");\n try {\n await this.store.ensureLoaded();\n this.status.set(\"ready\");\n this.liveRefresh.start();\n } catch (error) {\n this.loadError.set(workspaceErrorMessage(error));\n this.status.set(\"error\");\n }\n }\n\n private async refresh(): Promise<void> {\n this.refreshError.set(null);\n try {\n await this.store.reload();\n // The ticks belonged to the list that was on screen.\n this.selection.exit();\n } catch (error) {\n this.refreshError.set(this.translate(workspaceErrorMessage(error)));\n }\n }\n\n protected async loadMore(): Promise<void> {\n if (this.loadingMore()) return;\n this.loadMoreError.set(null);\n this.loadingMore.set(true);\n try {\n await this.store.loadMore();\n } catch (error) {\n this.loadMoreError.set(this.translate(workspaceErrorMessage(error)));\n } finally {\n this.loadingMore.set(false);\n }\n }\n\n protected onTabChange(tab: string | undefined): void {\n if (tab) this.filter.tab.set(tab as WorkspaceTab);\n }\n\n protected deleteSelected(): void {\n void this.confirmDelete(this.selection.selectedItems());\n }\n\n /**\n * Asks, then deletes. The API only has a bulk DELETE, so one workspace is a\n * one-element batch. If some could not be deleted the dialog stays open and says\n * which, and a retry only targets those, so it does not re-attempt ones already gone.\n */\n protected async confirmDelete(targets: Workspace[]): Promise<void> {\n if (!targets.length) return;\n const names = new Map(targets.map(w => [w.id, w.name]));\n let remaining = targets.map(w => w.id);\n\n const single = targets.length === 1;\n const subject = single ? targets[0].name : this.transloco.translate(\"documentWorkspaces.list.workspaceCount\", { count: targets.length });\n const config: ConfirmDialogConfig = {\n title: single\n ? this.transloco.translate(\"documentWorkspaces.list.deleteTitle\")\n : this.transloco.translate(\"documentWorkspaces.list.deleteWorkspacesTitle\", { count: targets.length }),\n description: this.transloco.translate(\"documentWorkspaces.common.deleteWarning\", { name: subject }),\n emphasis: subject,\n destructive: true,\n action: async () => {\n const { failed } = await this.store.remove(remaining);\n remaining = failed.map(f => f.id);\n if (failed.length) {\n throw new Error(\n this.transloco.translate(\"documentWorkspaces.list.bulkDeleteFailed\", {\n count: failed.length,\n names: failed.map(f => names.get(f.id) ?? f.id).join(\", \")\n })\n );\n }\n }\n };\n\n const result = await ConfirmAction.call(config, { injector: this.injector });\n if (result === \"dialog-confirm\" && !single) this.selection.exit();\n }\n\n private translate(message: Message): string {\n return \"key\" in message ? this.transloco.translate(message.key, message.params) : message.text;\n }\n}\n","import { ChangeDetectionStrategy, Component, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckboxComponent, TagComponent, TrashIcon, UploadIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport { injectDocumentLabels } from \"../../composables/inject-document-labels\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { WorkspaceDocument, WorkspaceRole } from \"../../models/workspace.model\";\nimport { DocumentStatusBadge } from \"../shared/document-status-badge\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\nimport { HighlightedText } from \"../shared/highlighted-text\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\n\n/**\n * A document in the grid.\n *\n * Like `WorkspaceCard` it only reports what the user asked for — open it,\n * delete it, tick it — and leaves doing it to the detail view. The name is a real\n * button so the keyboard reaches it; the whole card is clickable for the pointer.\n */\n@Component({\n selector: \"document-card\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n CheckboxComponent,\n DocumentStatusBadge,\n DocumentTypeIcon,\n HighlightedText,\n TagComponent,\n TooltipDirective,\n TrashIcon,\n UploadIcon,\n WorkspaceMessage,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class:\n \"group border-(--stroke-primary-lighter) bg-(--bg-neutral-white) relative flex cursor-pointer flex-col rounded-(--radius-md) border p-4 transition-shadow hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)] data-active:border-(--stroke-primary-base) data-selected:border-(--stroke-primary-base) data-selected:bg-(--bg-primary-lightest) data-locked:cursor-default\",\n \"[attr.data-status]\": \"document().status ?? null\",\n \"[attr.data-active]\": \"active() ? '' : null\",\n \"[attr.data-selected]\": \"selected() ? '' : null\",\n \"[attr.data-locked]\": \"selecting() && !selectable() ? '' : null\",\n \"(click)\": \"onCardClick($event)\"\n },\n template: `\n <div class=\"mb-3 flex items-start gap-3\">\n <document-type-icon [type]=\"document().type\" />\n <span class=\"flex-1\"></span>\n @if (selecting()) {\n <checkbox\n [checked]=\"selected()\"\n [disabled]=\"!selectable()\"\n [aria-label]=\"document().name\"\n (checkedChange)=\"selectedChange.emit($event)\" />\n } @else {\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n class=\"text-(--font-error-muted) opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-(--bg-error-lightest) hover:text-(--font-error-base) max-md:opacity-100\"\n [iconOnly]=\"true\"\n [writerOnly]=\"workspaceRole()\"\n [attr.aria-label]=\"'documentWorkspaces.detail.deleteDocument' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.delete' | transloco\"\n (click)=\"deleteRequested.emit()\">\n <trash-icon />\n </button>\n }\n </div>\n\n <h3 class=\"mb-2 truncate text-sm leading-[18px] font-semibold text-(--font-neutral-black)\">\n <button variant=\"none\" type=\"button\" class=\"block max-w-full truncate text-left\" [attr.title]=\"document().name\" (click)=\"activate()\">\n <highlighted-text [text]=\"document().name\" [query]=\"query()\" />\n </button>\n </h3>\n <span class=\"min-h-4 text-xs text-(--font-primary-muted)\" data-testid=\"size\">{{ labels.size() ?? \"—\" }}</span>\n\n <div class=\"my-3 h-px bg-(--stroke-primary-lighter)\"></div>\n <div class=\"flex items-center justify-between gap-2 text-xs\">\n <tag scheme=\"grey\" size=\"xs\" class=\"min-w-0\" [hidden]=\"!document().uploadedBy && !document().source\">\n @if (document().uploadedBy) {\n <upload-icon class=\"size-3 shrink-0\" />\n <span class=\"truncate\">{{ \"documentWorkspaces.detail.uploadedBy\" | transloco: { name: document().uploadedBy } }}</span>\n } @else {\n <span class=\"truncate\">{{ document().source }}</span>\n }\n </tag>\n @if (document().status) {\n <document-status-badge class=\"ml-auto\" [status]=\"document().status\" />\n } @else if (labels.added(); as added) {\n <span class=\"ml-auto shrink-0 text-(--font-primary-muted)\"><workspace-message [message]=\"added\" /></span>\n }\n </div>\n `\n})\nexport class DocumentCard {\n readonly document = input.required<WorkspaceDocument>();\n /** The role of the current user in the workspace — the delete button is for writers. */\n readonly workspaceRole = input.required<WorkspaceRole | null | undefined>();\n /** The search box's text, to mark in the name. */\n readonly query = input(\"\");\n /** The one open in the preview. */\n readonly active = input(false);\n readonly selecting = input(false);\n readonly selected = input(false);\n readonly selectable = input(true);\n\n readonly opened = output<void>();\n readonly deleteRequested = output<void>();\n readonly selectedChange = output<boolean>();\n\n protected readonly labels = injectDocumentLabels(this.document);\n\n /** Opens the document — or, in selection mode, ticks it. */\n protected activate(): void {\n if (this.selecting()) {\n if (this.selectable()) this.selectedChange.emit(!this.selected());\n } else {\n this.opened.emit();\n }\n }\n\n protected onCardClick(event: MouseEvent): void {\n // A button or the checkbox has handled it already.\n if ((event.target as HTMLElement).closest(\"button, input, label\")) return;\n this.activate();\n }\n}\n","import { ChangeDetectionStrategy, Component, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckboxComponent, TagComponent, TrashIcon, UploadIcon, TooltipDirective } from \"@sinequa/galactik\";\nimport { injectDocumentLabels } from \"../../composables/inject-document-labels\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { WorkspaceDocument, WorkspaceRole } from \"../../models/workspace.model\";\nimport { DocumentStatusBadge } from \"../shared/document-status-badge\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\nimport { HighlightedText } from \"../shared/highlighted-text\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\n\n/**\n * The columns of the document list — the header and every row share it.\n * Written out in full: Tailwind only generates the classes it can read as literals.\n */\nexport const DOCUMENT_ROW_COLUMNS =\n \"grid grid-cols-[minmax(0,1fr)_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_minmax(0,1fr)_6rem_10rem_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_minmax(0,1fr)_6rem_10rem_2.5rem]\";\n\n/** A document in the list view. Same contract as `DocumentCard`. */\n@Component({\n selector: \"document-row\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n CheckboxComponent,\n DocumentStatusBadge,\n DocumentTypeIcon,\n HighlightedText,\n TagComponent,\n TooltipDirective,\n TrashIcon,\n UploadIcon,\n WorkspaceMessage,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n role: \"row\",\n class: `${DOCUMENT_ROW_COLUMNS} group border-(--stroke-primary-lighter) bg-(--bg-neutral-white) cursor-pointer rounded-(--radius-md) border py-2.5 text-sm transition-shadow hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)] data-active:border-(--stroke-primary-base) data-selected:border-(--stroke-primary-base) data-selected:bg-(--bg-primary-lightest) data-locked:cursor-default`,\n \"[attr.data-status]\": \"document().status ?? null\",\n \"[attr.data-selecting]\": \"selecting() ? '' : null\",\n \"[attr.data-active]\": \"active() ? '' : null\",\n \"[attr.data-selected]\": \"selected() ? '' : null\",\n \"[attr.data-locked]\": \"selecting() && !selectable() ? '' : null\",\n \"(click)\": \"onRowClick($event)\"\n },\n template: `\n @if (selecting()) {\n <div role=\"cell\">\n <checkbox\n [checked]=\"selected()\"\n [disabled]=\"!selectable()\"\n [aria-label]=\"document().name\"\n (checkedChange)=\"selectedChange.emit($event)\" />\n </div>\n }\n\n <div role=\"cell\" class=\"flex min-w-0 items-center gap-3\">\n <document-type-icon size=\"md\" [type]=\"document().type\" />\n <button variant=\"none\" type=\"button\" class=\"block max-w-full truncate text-left font-semibold text-(--font-neutral-black)\" (click)=\"activate()\">\n <highlighted-text [text]=\"document().name\" [query]=\"query()\" />\n </button>\n </div>\n\n <div role=\"cell\" class=\"min-w-0 @max-3xl:hidden\">\n @if (document().uploadedBy) {\n <tag scheme=\"grey\" size=\"xs\">\n <upload-icon class=\"size-3 shrink-0\" />\n <span class=\"truncate\">{{ \"documentWorkspaces.detail.uploadedBy\" | transloco: { name: document().uploadedBy } }}</span>\n </tag>\n } @else if (document().source) {\n <tag scheme=\"grey\" size=\"xs\">{{ document().source }}</tag>\n }\n </div>\n\n <div role=\"cell\" class=\"text-xs text-(--font-primary-muted) @max-3xl:hidden\" data-testid=\"size\">{{ labels.size() ?? \"—\" }}</div>\n\n <div role=\"cell\" class=\"text-xs text-(--font-primary-base)\">\n @if (document().status) {\n <document-status-badge [status]=\"document().status\" />\n } @else if (labels.added(); as added) {\n <workspace-message [message]=\"added\" />\n }\n </div>\n\n <div role=\"cell\" class=\"flex justify-end\">\n @if (!selecting()) {\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n class=\"text-(--font-error-muted) opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-(--bg-error-lightest) hover:text-(--font-error-base) max-md:opacity-100\"\n [iconOnly]=\"true\"\n [writerOnly]=\"workspaceRole()\"\n [attr.aria-label]=\"'documentWorkspaces.detail.deleteDocument' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.delete' | transloco\"\n (click)=\"deleteRequested.emit()\">\n <trash-icon />\n </button>\n }\n </div>\n `\n})\nexport class DocumentRow {\n readonly document = input.required<WorkspaceDocument>();\n readonly workspaceRole = input.required<WorkspaceRole | null | undefined>();\n readonly query = input(\"\");\n readonly active = input(false);\n readonly selecting = input(false);\n readonly selected = input(false);\n readonly selectable = input(true);\n\n readonly opened = output<void>();\n readonly deleteRequested = output<void>();\n readonly selectedChange = output<boolean>();\n\n protected readonly labels = injectDocumentLabels(this.document);\n\n protected activate(): void {\n if (this.selecting()) {\n if (this.selectable()) this.selectedChange.emit(!this.selected());\n } else {\n this.opened.emit();\n }\n }\n\n protected onRowClick(event: MouseEvent): void {\n if ((event.target as HTMLElement).closest(\"button, input, label\")) return;\n this.activate();\n }\n}\n","import type { AssignableRole, Member } from \"../models/share.model\";\nimport type { Workspace } from \"../models/workspace.model\";\nimport type { ResolvedPrincipal } from \"../services/principal-directory.service\";\n\ntype Access = Pick<Workspace, \"creatorId\" | \"ownerIds\" | \"readerIds\">;\n\n/**\n * The access list of a workspace: one row per distinct id, the creator pinned to\n * the top and everyone else by name.\n *\n * The creator is normally in `ownerIds` as well, so without the dedupe they would\n * show up twice — once as the immutable creator and once as an ordinary owner.\n * An id in both arrays counts as an owner.\n */\nexport function buildMembers(workspace: Access, resolve: (id: string) => ResolvedPrincipal): Member[] {\n const seen = new Set<string>();\n const members: Member[] = [];\n const add = (id: string, role: Member[\"role\"]): void => {\n if (!id || seen.has(id)) return;\n seen.add(id);\n const { name, email, isGroup, resolved, isSelf } = resolve(id);\n members.push({ id, name, email, isGroup, isSelf, role, resolved });\n };\n add(workspace.creatorId, \"creator\");\n workspace.ownerIds.forEach(id => add(id, \"owner\"));\n workspace.readerIds.forEach(id => add(id, \"reader\"));\n\n return members.sort((a, b) => Number(b.role === \"creator\") - Number(a.role === \"creator\") || a.name.localeCompare(b.name));\n}\n\n/**\n * The `owners`/`readers` arrays after giving `id` a role, or taking its access away.\n *\n * The API replaces both arrays wholesale, so they carry every id the workspace\n * already holds — the creator's, and ids that could not be resolved to a name —\n * not just the ones the list shows: dropping one would silently revoke it.\n */\nexport function withRole(\n workspace: Pick<Workspace, \"ownerIds\" | \"readerIds\">,\n id: string,\n role: AssignableRole | \"remove\"\n): { owners: string[]; readers: string[] } {\n const owners = workspace.ownerIds.filter(o => o !== id);\n const readers = workspace.readerIds.filter(r => r !== id);\n if (role === \"owner\") owners.push(id);\n else if (role === \"reader\") readers.push(id);\n return { owners, readers };\n}\n\n/** The arrays after adding the picked people: the ids already there stay, the picked ones join their role's array. */\nexport function withAdded(\n workspace: Pick<Workspace, \"ownerIds\" | \"readerIds\">,\n picked: readonly { id: string; role: AssignableRole }[]\n): { owners: string[]; readers: string[] } {\n const ids = new Set(picked.map(p => p.id));\n return {\n owners: [...workspace.ownerIds.filter(id => !ids.has(id)), ...picked.filter(p => p.role === \"owner\").map(p => p.id)],\n readers: [...workspace.readerIds.filter(id => !ids.has(id)), ...picked.filter(p => p.role === \"reader\").map(p => p.id)]\n };\n}\n\n/** `AB` for \"Alice Brown\", `AL` for \"alice\", `?` for nothing. */\nexport function initialsOf(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (!parts.length) return \"?\";\n return (parts.length === 1 ? parts[0].slice(0, 2) : parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from \"@angular/core\";\nimport { AvatarComponent, AvatarFallbackComponent, UserRoundIcon } from \"@sinequa/galactik\";\nimport { initialsOf } from \"../../utils/members\";\n\n/** A small round avatar: the initials of a person, a glyph for a group. */\n@Component({\n selector: \"principal-avatar\",\n imports: [AvatarComponent, AvatarFallbackComponent, UserRoundIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"inline-flex shrink-0\", \"aria-hidden\": \"true\" },\n template: `\n <avatar size=\"medium\" fill=\"secondary\" scheme=\"sage\">\n <AvatarFallback>\n @if (group()) {\n <user-round-icon />\n } @else {\n {{ initials() }}\n }\n </AvatarFallback>\n </avatar>\n `\n})\nexport class PrincipalAvatar {\n readonly name = input.required<string>();\n readonly group = input(false);\n\n protected readonly initials = computed(() => initialsOf(this.name()));\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n ChevronDownIcon,\n EyeIcon,\n KeyIcon,\n MenuComponent,\n MenuContentComponent,\n MenuItemComponent,\n MenuTriggerDirective\n} from \"@sinequa/galactik\";\nimport type { AssignableRole } from \"../../models/share.model\";\n\n/**\n * \"Owner ▾\" / \"Reader ▾\": a menu to pick a role, and — for someone who already has\n * access — to take it away.\n *\n * It only reports the choice; whoever uses it decides what it does.\n */\n@Component({\n selector: \"role-menu\",\n imports: [TranslocoPipe, ButtonComponent, ChevronDownIcon, EyeIcon, KeyIcon, MenuComponent, MenuContentComponent, MenuItemComponent, MenuTriggerDirective],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"inline-flex\" },\n template: `\n <button type=\"button\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [menuTrigger]=\"menu\" [disabled]=\"disabled()\" [attr.aria-label]=\"label()\">\n @if (role() === \"owner\") {\n <key-icon />\n } @else {\n <eye-icon />\n }\n {{ roleKey() | transloco }}\n <chevron-down-icon class=\"size-3\" />\n </button>\n <Menu #menu=\"menu\" placement=\"bottom-end\" (itemSelected)=\"onSelect($event)\">\n <MenuContent>\n <MenuItem value=\"owner\">\n <key-icon />\n {{ \"documentWorkspaces.share.owner\" | transloco }}\n </MenuItem>\n <MenuItem value=\"reader\">\n <eye-icon />\n {{ \"documentWorkspaces.share.reader\" | transloco }}\n </MenuItem>\n @if (removable()) {\n <MenuItem value=\"remove\" class=\"text-(--font-error-base)\">{{ \"documentWorkspaces.common.remove\" | transloco }}</MenuItem>\n }\n </MenuContent>\n </Menu>\n `\n})\nexport class RoleMenu {\n readonly role = input.required<AssignableRole>();\n /** Adds \"Remove\" to the menu. */\n readonly removable = input(false);\n readonly disabled = input(false);\n /** The accessible name of the button, already translated. */\n readonly label = input.required<string>();\n\n readonly roleChange = output<AssignableRole>();\n readonly removeRequested = output<void>();\n\n protected readonly roleKey = computed(() => (this.role() === \"owner\" ? \"documentWorkspaces.share.owner\" : \"documentWorkspaces.share.reader\"));\n\n protected onSelect(value: string): void {\n if (value === \"remove\") this.removeRequested.emit();\n else this.roleChange.emit(value as AssignableRole);\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output, signal } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, EyeIcon, KeyIcon } from \"@sinequa/galactik\";\nimport type { AssignableRole, Member } from \"../../models/share.model\";\nimport { PrincipalAvatar } from \"./principal-avatar\";\nimport { RoleMenu } from \"./role-menu\";\n\n/**\n * One person of the access list.\n *\n * Two rows never get a role menu: the creator's (immutable for the API) and your own\n * (an owner who demotes or removes themselves loses the workspace with no way back).\n * Removing asks first, inline in the row.\n */\n@Component({\n selector: \"member-row\",\n imports: [TranslocoPipe, ButtonComponent, EyeIcon, KeyIcon, PrincipalAvatar, RoleMenu],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"flex items-center gap-3 py-2\", role: \"listitem\" },\n template: `\n @if (confirming()) {\n <span class=\"min-w-0 flex-1 text-sm\">{{ \"documentWorkspaces.share.confirmRemove\" | transloco: { name: member().name } }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"confirming.set(false)\">{{ \"documentWorkspaces.common.cancel\" | transloco }}</button>\n <button size=\"sm\" class=\"bg-(--bg-error-base) enabled:hover:bg-(--bg-error-base) enabled:hover:opacity-90\" [disabled]=\"disabled()\" (click)=\"removed.emit()\">{{ \"documentWorkspaces.common.remove\" | transloco }}</button>\n } @else {\n <principal-avatar [name]=\"member().name\" [group]=\"member().isGroup\" />\n <div class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate text-sm\" [class.text-(--font-primary-muted)]=\"!member().resolved\" [attr.title]=\"member().resolved ? null : ('documentWorkspaces.share.unresolvedName' | transloco)\">\n {{ member().name }}\n @if (member().isSelf) {\n <span class=\"text-(--font-primary-muted)\">{{ \"documentWorkspaces.share.you\" | transloco }}</span>\n }\n </span>\n @if (member().email) {\n <span class=\"text-(--font-primary-muted) truncate text-xs\">{{ member().email }}</span>\n }\n </div>\n\n @if (member().role === \"creator\" || member().isSelf) {\n <span class=\"text-(--font-primary-muted) inline-flex items-center gap-1 text-sm\">\n @if (member().role === \"reader\") {\n <eye-icon class=\"size-4\" />\n {{ \"documentWorkspaces.share.reader\" | transloco }}\n } @else {\n <key-icon class=\"size-4\" />\n {{ \"documentWorkspaces.share.owner\" | transloco }}\n }\n </span>\n } @else {\n <role-menu\n [role]=\"assignable()\"\n [removable]=\"true\"\n [disabled]=\"disabled()\"\n [label]=\"'documentWorkspaces.share.changeRole' | transloco: { name: member().name }\"\n (roleChange)=\"roleChange.emit($event)\"\n (removeRequested)=\"confirming.set(true)\" />\n }\n }\n `\n})\nexport class MemberRow {\n readonly member = input.required<Member>();\n /** A change is in flight: nothing else can be asked meanwhile. */\n readonly disabled = input(false);\n\n readonly roleChange = output<AssignableRole>();\n readonly removed = output<void>();\n\n protected readonly confirming = signal(false);\n\n protected readonly assignable = computed<AssignableRole>(() => (this.member().role === \"reader\" ? \"reader\" : \"owner\"));\n}\n","import { ChangeDetectionStrategy, Component, inject, input, model, signal } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport type { SecurityPrincipal } from \"@sinequa/atomic\";\nimport { AutocompleteComponent, TagComponent, TagRemoveComponent } from \"@sinequa/galactik\";\nimport type { AssignableRole, PickedPrincipal } from \"../../models/share.model\";\nimport { PrincipalDirectory, principalDisplayName } from \"../../services/principal-directory.service\";\nimport { PrincipalAvatar } from \"./principal-avatar\";\nimport { RoleMenu } from \"./role-menu\";\n\n/**\n * Picks people: a tag-style box with search-as-you-type over the directory, a chip\n * per person, and the role the next ones get. The picked people are the `entries`\n * model — a parent owns them and can clear them.\n *\n * Groups never show up in the results: the API documents `owners`/`readers` as\n * *user* ids, so staging a group would look like it worked and grant nobody anything.\n *\n * The id of an entry is the principal's `userId` (`domain|id`), which is what a\n * workspace holds and the only form the API accepts.\n *\n * The search, the results popup (a combobox in the top layer, so a dialog's edges cannot clip it) and its keyboard\n * are those of the library's `Autocomplete`: this component adds what is specific to people — the avatar in a row, the\n * role of a chip, the role the next ones get. Like in a chat's mention box, Enter picks the first row and closes the\n * list.\n */\n@Component({\n selector: \"principal-picker\",\n imports: [TranslocoPipe, AutocompleteComponent, PrincipalAvatar, RoleMenu, TagComponent, TagRemoveComponent],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block\" },\n template: `\n <Autocomplete\n [selected]=\"entries()\"\n [loader]=\"search\"\n [key]=\"principalKey\"\n [label]=\"displayName\"\n [selectedKey]=\"entryKey\"\n [selectedLabel]=\"entryLabel\"\n [excludeKeys]=\"excludeIds()\"\n [indicator]=\"false\"\n [closeOnPick]=\"true\"\n [placeholder]=\"'documentWorkspaces.share.searchPlaceholder' | transloco\"\n [ariaLabel]=\"'documentWorkspaces.share.searchUsers' | transloco\"\n [noResultsText]=\"'documentWorkspaces.share.noMatches' | transloco\"\n [searchingText]=\"'documentWorkspaces.share.searching' | transloco\"\n [errorText]=\"'documentWorkspaces.share.searchFailed' | transloco\"\n (picked)=\"pick($event)\"\n (removed)=\"removeEntry($event)\">\n <ng-template #autocompleteOption let-person>\n <principal-avatar [name]=\"displayName(person)\" />\n <span class=\"flex min-w-0 flex-col\">\n <span class=\"text-(--font-primary-base) truncate text-sm font-medium\">{{ displayName(person) }}</span>\n <span class=\"text-(--font-primary-muted) truncate text-xs\">{{ person.longName }}</span>\n </span>\n </ng-template>\n\n <ng-template #autocompleteTag let-entry let-i=\"index\">\n <tag scheme=\"grey\" size=\"xs\" data-testid=\"chip\">\n {{ entry.name }}\n @if (showChipRole()) {\n <!-- A toggle rather than a menu: with two roles to choose from, a menu costs a click to say what one button already says. -->\n <button\n type=\"button\"\n class=\"text-(--font-primary-base) underline\"\n [attr.aria-label]=\"(entry.role === 'owner' ? 'documentWorkspaces.share.changeRoleToReader' : 'documentWorkspaces.share.changeRoleToOwner') | transloco: { name: entry.name }\"\n (click)=\"toggleRole(i)\">\n {{ (entry.role === \"owner\" ? \"documentWorkspaces.share.owner\" : \"documentWorkspaces.share.reader\") | transloco }}\n </button>\n }\n <tag-remove [aria-label]=\"'documentWorkspaces.share.removeUser' | transloco: { name: entry.name }\" (removed)=\"removeEntry(entry)\" />\n </tag>\n </ng-template>\n\n <role-menu\n autocompleteSuffix\n [role]=\"role()\"\n [label]=\"'documentWorkspaces.share.roleLabel' | transloco\"\n (roleChange)=\"role.set($event)\" />\n </Autocomplete>\n `\n})\nexport class PrincipalPicker {\n /** The people picked so far. */\n readonly entries = model<PickedPrincipal[]>([]);\n /** Ids to keep out of the results — the people who already have access. */\n readonly excludeIds = input<readonly string[]>([]);\n /** Chips carry a toggle to switch their role. Off where everyone picked takes the selector's role. */\n readonly showChipRole = input(false);\n\n private readonly directory = inject(PrincipalDirectory);\n\n /** The role the next person picked gets. */\n protected readonly role = signal<AssignableRole>(\"reader\");\n\n /** Groups are not offered: see the class comment. */\n protected readonly search = (query: string, { signal }: { signal: AbortSignal }) =>\n this.directory.search(query, signal).then(principals => principals.filter(p => !p.isGroup));\n\n protected readonly principalKey = (person: SecurityPrincipal) => person.userId;\n protected readonly entryKey = (entry: PickedPrincipal) => entry.id;\n protected readonly entryLabel = (entry: PickedPrincipal) => entry.name;\n protected readonly displayName = (person: SecurityPrincipal) => principalDisplayName(person);\n\n protected pick(person: SecurityPrincipal): void {\n if (this.entries().some(e => e.id === person.userId)) return;\n this.entries.update(entries => [...entries, { id: person.userId, name: principalDisplayName(person), isGroup: person.isGroup, role: this.role() }]);\n }\n\n protected toggleRole(index: number): void {\n this.entries.update(entries => entries.map((e, i) => (i === index ? { ...e, role: e.role === \"owner\" ? \"reader\" : \"owner\" } : e)));\n }\n\n protected removeEntry(entry: PickedPrincipal): void {\n this.entries.update(entries => entries.filter(e => e.id !== entry.id));\n }\n}\n","import { afterNextRender, ChangeDetectionStrategy, Component, computed, inject, signal, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n createCallable,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogResult,\n DialogTitleComponent,\n injectCallRef\n} from \"@sinequa/galactik\";\nimport type { AssignableRole, PickedPrincipal } from \"../../models/share.model\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { CurrentUserService } from \"../../services/current-user.service\";\nimport { PrincipalDirectory } from \"../../services/principal-directory.service\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { workspaceErrorMessage, WorkspaceErrorMessage } from \"../../utils/error-message\";\nimport { buildMembers, withAdded, withRole } from \"../../utils/members\";\nimport { MemberRow } from \"./member-row\";\nimport { PrincipalPicker } from \"./principal-picker\";\n\nexport interface ShareDialogConfig {\n workspaceId: Workspace[\"id\"];\n}\n\n/**\n * \"Manage access\": who can do what in a workspace.\n *\n * ```ts\n * await ShareWorkspace.call({ workspaceId });\n * ```\n *\n * Every change — a role, a removal, people added — is saved at once. The list reads\n * the workspace from `WorkspacesStore`, so it shows what the server now holds, and\n * stays in step with a change made elsewhere. Only the ones who can write to the\n * workspace get here; the server refuses the others anyway.\n */\n@Component({\n selector: \"share-dialog\",\n imports: [\n TranslocoPipe,\n ButtonComponent,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogTitleComponent,\n MemberRow,\n PrincipalPicker\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <dialog #dialog (closed)=\"call.end($event)\">\n <DialogContent size=\"md\">\n <DialogHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\">\n <DialogTitle>{{ \"documentWorkspaces.share.title\" | transloco }}</DialogTitle>\n </DialogHeader>\n <DialogBody>\n @if (workspace(); as workspace) {\n <p class=\"text-(--font-primary-muted) mb-3 text-sm\">{{ workspace.name }}</p>\n\n <div class=\"flex items-start gap-2\">\n <principal-picker class=\"flex-1\" [(entries)]=\"staged\" [excludeIds]=\"memberIds()\" />\n <button [disabled]=\"!staged().length || busy()\" (click)=\"add()\">\n {{ (adding() ? \"documentWorkspaces.share.adding\" : \"documentWorkspaces.share.addPeople\") | transloco }}\n </button>\n </div>\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">\n @if (\"key\" in message) {\n {{ message.key | transloco }}\n } @else {\n {{ message.text }}\n }\n </p>\n }\n\n <p class=\"text-(--font-primary-muted) text-xs\" aria-live=\"polite\">{{ \"documentWorkspaces.share.accessCount\" | transloco: { count: members().length } }}</p>\n <div role=\"list\" class=\"divide-(--stroke-primary-lighter) max-h-72 divide-y overflow-auto\" data-testid=\"members\">\n @for (member of members(); track member.id) {\n <member-row [member]=\"member\" [disabled]=\"busy()\" (roleChange)=\"change(member.id, $event)\" (removed)=\"change(member.id, 'remove')\" />\n }\n </div>\n\n <p class=\"text-(--font-primary-muted) text-xs\">{{ \"documentWorkspaces.share.accessNotice\" | transloco }}</p>\n }\n </DialogBody>\n\n <DialogFooter>\n <button variant=\"secondary\" (click)=\"dialog.close('dialog-close')\">{{ \"documentWorkspaces.common.close\" | transloco }}</button>\n </DialogFooter>\n </DialogContent>\n </dialog>\n `\n})\nexport class ShareDialog {\n protected readonly call = injectCallRef<ShareDialogConfig, DialogResult>();\n\n private readonly dialog = viewChild.required(DialogComponent);\n private readonly store = inject(WorkspacesStore);\n private readonly directory = inject(PrincipalDirectory);\n // Read so the list re-renders once the logged-in user is known: it decides whose row has no role menu.\n private readonly currentUser = inject(CurrentUserService);\n\n private readonly workspaceId = computed(() => this.call.props().workspaceId);\n protected readonly workspace = computed(() => this.store.workspace(this.workspaceId()));\n protected readonly members = computed(() => {\n this.currentUser.user();\n const workspace = this.workspace();\n return workspace ? buildMembers(workspace, id => this.directory.resolve(id)) : [];\n });\n protected readonly memberIds = computed(() => this.members().map(m => m.id));\n\n /** The people picked, not added yet. */\n protected readonly staged = signal<PickedPrincipal[]>([]);\n protected readonly adding = signal(false);\n protected readonly busy = signal(false);\n protected readonly error = signal<WorkspaceErrorMessage | null>(null);\n\n constructor() {\n afterNextRender(() => {\n this.dialog().showModal();\n // Names arrive after the first paint; the list shows raw ids until then.\n void this.directory.ensureResolved(this.memberIds());\n });\n }\n\n protected async add(): Promise<void> {\n const workspace = this.workspace();\n const picked = this.staged();\n if (!workspace || !picked.length) return;\n this.adding.set(true);\n await this.save(\n () => withAdded(workspace, picked),\n () => this.staged.set([])\n );\n this.adding.set(false);\n }\n\n protected async change(id: string, role: AssignableRole | \"remove\"): Promise<void> {\n const workspace = this.workspace();\n if (!workspace) return;\n await this.save(() => withRole(workspace, id, role));\n }\n\n /** One write at a time: the arrays it sends are built from the workspace as it is now. */\n private async save(access: () => { owners: string[]; readers: string[] }, onSaved?: () => void): Promise<void> {\n if (this.busy()) return;\n this.busy.set(true);\n this.error.set(null);\n try {\n const updated = await this.store.update(this.workspaceId(), access());\n if (!updated) {\n this.error.set({ key: \"documentWorkspaces.errors.notFound\" });\n return;\n }\n onSaved?.();\n } catch (error) {\n this.error.set(workspaceErrorMessage(error));\n } finally {\n this.busy.set(false);\n }\n }\n}\n\n/** Imperative entry point: `await ShareWorkspace.call({ workspaceId })`. */\nexport const ShareWorkspace = createCallable<ShareDialogConfig, DialogResult>(ShareDialog);\n","import { ChangeDetectionStrategy, Component, computed, effect, inject, input, output, signal, untracked, viewChild, ElementRef } from \"@angular/core\";\nimport { toSignal } from \"@angular/core/rxjs-interop\";\nimport { disabled, form, FormField } from \"@angular/forms/signals\";\nimport { DomSanitizer, SafeResourceUrl } from \"@angular/platform-browser\";\nimport { provideTranslocoScope, TranslocoPipe, TranslocoService } from \"@jsverse/transloco\";\nimport { fetchPreview } from \"@sinequa/atomic\";\nimport {\n ButtonComponent,\n ChatPlusIcon,\n ChevronDownIcon,\n ChevronUpIcon,\n ExternalLinkIcon,\n MagnifyingGlassMinusIcon,\n MagnifyingGlassPlusIcon,\n SearchInputComponent,\n XMarkIcon,\n TooltipDirective\n} from \"@sinequa/galactik\";\nimport { injectTextSearch } from \"../../composables/inject-text-search\";\nimport type { WorkspaceDocument } from \"../../models/workspace.model\";\nimport { resolveEndpointUrl } from \"../../utils/base-url\";\nimport { formatDate } from \"../../utils/format\";\nimport { DocumentTypeIcon } from \"../shared/document-type-icon\";\n\n/** What a search app falls back to when the app config names no query. */\nconst DEFAULT_QUERY_NAME = \"_query\";\n\nconst ZOOM_STEP = 0.1;\nconst ZOOM_MIN = 0.25;\nconst ZOOM_MAX = 2.5;\nconst ZOOM_DEFAULT = 0.75;\n\n/** What \"Ask follow-up\" hands the host: what to show in its compose area, already translated. */\nexport interface AskFollowUp {\n title: string;\n type: WorkspaceDocument[\"type\"];\n placeholder: string;\n suggestions: string[];\n}\n\n/**\n * The preview of a document: the converted copy the preview web service points at,\n * in a frame, with a zoom, a find-in-text and an \"Ask follow-up\".\n *\n * It asks the service for the document by id, as a search app does. Until the\n * backend hands out the index id for an uploaded document (the listing carries\n * the upload-side id) the service answers an error, and the panel says the document\n * cannot be previewed rather than showing a stand-in.\n *\n * Finding text needs to read the frame, so it only works when the copy is served\n * from the same origin as the page; otherwise the box is disabled and says why.\n */\n@Component({\n selector: \"document-preview-panel\",\n imports: [\n TranslocoPipe,\n FormField,\n ButtonComponent,\n ChatPlusIcon,\n ChevronDownIcon,\n ChevronUpIcon,\n DocumentTypeIcon,\n ExternalLinkIcon,\n MagnifyingGlassMinusIcon,\n MagnifyingGlassPlusIcon,\n SearchInputComponent,\n TooltipDirective,\n XMarkIcon\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"flex min-h-0 flex-col bg-(--bg-neutral-white)\" },\n template: `\n <header class=\"flex flex-col gap-3 border-b border-(--stroke-primary-lighter) p-4\">\n <div class=\"flex items-start gap-3\">\n <document-type-icon size=\"md\" [type]=\"document().type\" />\n <h2 class=\"min-w-0 flex-1 pt-1.5 text-base leading-[22px] font-semibold text-(--font-neutral-black)\">\n @if (titleUrl(); as url) {\n <a class=\"inline-flex max-w-full items-center gap-1 hover:underline\" [href]=\"url\" target=\"_blank\" rel=\"noopener\">\n <span class=\"truncate\">{{ document().name }}</span>\n <external-link-icon class=\"size-3.5 shrink-0\" />\n </a>\n } @else {\n <span class=\"block truncate\" data-testid=\"title\">{{ document().name }}</span>\n }\n </h2>\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'documentWorkspaces.preview.close' | transloco\"\n [tooltip]=\"'documentWorkspaces.preview.close' | transloco\"\n (click)=\"closed.emit()\">\n <x-mark-icon />\n </button>\n </div>\n\n <dl class=\"flex flex-col gap-1 text-xs text-(--font-primary-base)\">\n @if (date(); as date) {\n <div class=\"flex gap-1\" data-testid=\"date\">\n <dt class=\"font-semibold\">{{ \"documentWorkspaces.preview.dateLabel\" | transloco }}</dt>\n <dd>{{ date }}</dd>\n </div>\n }\n @if (document().source) {\n <div class=\"flex gap-1\">\n <dt class=\"font-semibold\">{{ \"documentWorkspaces.preview.sourceLabel\" | transloco }}</dt>\n <dd>{{ document().source }}</dd>\n </div>\n }\n </dl>\n\n <div class=\"flex flex-wrap items-center gap-2\">\n <SearchInput\n role=\"search\"\n class=\"min-w-40 flex-[1_1_12rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.preview.findPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.preview.findPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\"\n [attr.title]=\"searchable() ? null : ('documentWorkspaces.preview.searchUnavailable' | transloco)\"\n (keydown)=\"onSearchKeydown($event)\">\n @if (query()) {\n <span class=\"shrink-0 text-xs text-(--font-primary-muted)\" aria-live=\"polite\" data-testid=\"count\">\n @if (search.matches()) {\n {{ \"documentWorkspaces.preview.searchCount\" | transloco: { current: search.activeIndex() + 1, total: search.matches() } }}\n } @else {\n {{ \"documentWorkspaces.preview.noResults\" | transloco }}\n }\n </span>\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"!search.matches()\"\n [attr.aria-label]=\"'documentWorkspaces.preview.previousResult' | transloco\"\n (click)=\"search.previous()\">\n <chevron-up-icon />\n </button>\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"!search.matches()\"\n [attr.aria-label]=\"'documentWorkspaces.preview.nextResult' | transloco\"\n (click)=\"search.next()\">\n <chevron-down-icon />\n </button>\n }\n </SearchInput>\n\n <button variant=\"secondary\" size=\"sm\" (click)=\"askFollowUpClicked()\">\n <chat-plus-icon />\n {{ \"documentWorkspaces.preview.askFollowUp\" | transloco }}\n </button>\n\n <div class=\"flex items-center\">\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"zoom() <= zoomMin\"\n [attr.aria-label]=\"'documentWorkspaces.preview.zoomOut' | transloco\"\n [tooltip]=\"'documentWorkspaces.preview.zoomOut' | transloco\"\n (click)=\"zoomBy(-1)\">\n <magnifying-glass-minus-icon />\n </button>\n <button\n variant=\"tertiary\"\n scheme=\"neutral\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"zoom() >= zoomMax\"\n [attr.aria-label]=\"'documentWorkspaces.preview.zoomIn' | transloco\"\n [tooltip]=\"'documentWorkspaces.preview.zoomIn' | transloco\"\n (click)=\"zoomBy(1)\">\n <magnifying-glass-plus-icon />\n </button>\n </div>\n </div>\n </header>\n\n <div class=\"relative min-h-0 flex-1 bg-(--bg-primary-lightest)\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"p-6 text-center text-sm text-(--font-primary-base)\" data-testid=\"loading\">{{ \"documentWorkspaces.preview.loading\" | transloco }}</p>\n }\n @case (\"unavailable\") {\n <p class=\"p-6 text-center text-sm text-(--font-primary-base)\" data-testid=\"unavailable\">{{ \"documentWorkspaces.preview.unavailable\" | transloco }}</p>\n }\n @case (\"ready\") {\n <!-- The frame is scaled, and its box stretched by the inverse so that, scaled, it still fills the panel. -->\n <div class=\"absolute inset-0 overflow-hidden\">\n <div class=\"origin-top-left\" [style.transform]=\"'scale(' + zoom() + ')'\" [style.width.%]=\"100 / zoom()\" [style.height.%]=\"100 / zoom()\">\n <iframe #frame class=\"size-full border-0 bg-white\" [title]=\"document().name\" [src]=\"frameUrl()\" (load)=\"onFrameLoad()\"></iframe>\n </div>\n </div>\n }\n }\n </div>\n `\n})\nexport class DocumentPreviewPanel {\n readonly document = input.required<WorkspaceDocument>();\n /** The query the preview web service renders the document for — the host's `query-name`. */\n readonly queryName = input(DEFAULT_QUERY_NAME);\n\n readonly closed = output<void>();\n readonly askFollowUp = output<AskFollowUp>();\n\n private readonly sanitizer = inject(DomSanitizer);\n private readonly transloco = inject(TranslocoService);\n private readonly lang = toSignal(this.transloco.langChanges$, { initialValue: this.transloco.getActiveLang() });\n private readonly frame = viewChild<ElementRef<HTMLIFrameElement>>(\"frame\");\n\n protected readonly zoomMin = ZOOM_MIN;\n protected readonly zoomMax = ZOOM_MAX;\n protected readonly zoom = signal(ZOOM_DEFAULT);\n protected readonly status = signal<\"loading\" | \"ready\" | \"unavailable\">(\"loading\");\n protected readonly frameUrl = signal<SafeResourceUrl | null>(null);\n protected readonly titleUrl = signal<string | undefined>(undefined);\n protected readonly date = computed(() => formatDate(this.document().createdAt, this.lang()));\n\n /** The text of the frame, once it has loaded and the page is allowed to read it. */\n private readonly frameBody = signal<Node | null>(null);\n protected readonly searchable = computed(() => this.frameBody() !== null);\n protected readonly query = signal(\"\");\n /** The find box, bound to `query`; off while the frame cannot be read. */\n protected readonly searchForm = form(this.query, path => disabled(path, () => !this.searchable()));\n protected readonly search = injectTextSearch({ root: this.frameBody, query: this.query });\n\n /** A slow answer for a document the user has already left must not replace the one on screen. */\n private token = 0;\n\n constructor() {\n effect(() => {\n const { id } = this.document();\n const queryName = this.queryName();\n untracked(() => void this.load(id, queryName));\n });\n }\n\n private async load(id: string, queryName: string): Promise<void> {\n const token = ++this.token;\n this.status.set(\"loading\");\n this.frameUrl.set(null);\n this.titleUrl.set(undefined);\n this.frameBody.set(null);\n this.query.set(\"\");\n\n try {\n const data = await fetchPreview(id, { name: queryName, text: \"\" });\n if (token !== this.token) return;\n if (!data.documentCachedContentUrl) {\n this.status.set(\"unavailable\");\n return;\n }\n // The URL is the preview service's own, resolved under the configured backend: trusted as a frame source.\n this.frameUrl.set(this.sanitizer.bypassSecurityTrustResourceUrl(resolveEndpointUrl(data.documentCachedContentUrl).href));\n this.titleUrl.set(data.record?.url1 || data.record?.originalUrl || undefined);\n this.status.set(\"ready\");\n } catch {\n // Expected until the backend exposes the index id of an uploaded document: say so, rather than fake one.\n if (token === this.token) this.status.set(\"unavailable\");\n }\n }\n\n protected onFrameLoad(): void {\n try {\n this.frameBody.set(this.frame()?.nativeElement.contentDocument?.body ?? null);\n } catch {\n // A copy served from another origin cannot be read: there is nothing to search.\n this.frameBody.set(null);\n }\n }\n\n protected zoomBy(direction: 1 | -1): void {\n this.zoom.update(zoom => Math.round(Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoom + direction * ZOOM_STEP)) * 100) / 100);\n }\n\n protected onSearchKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n if (event.shiftKey) this.search.previous();\n else this.search.next();\n } else if (event.key === \"Escape\" && this.query()) {\n // Clears the box; with nothing typed, Escape is the host's.\n event.stopPropagation();\n this.query.set(\"\");\n }\n }\n\n protected askFollowUpClicked(): void {\n const t = (key: string) => this.transloco.translate(`documentWorkspaces.preview.${key}`);\n this.askFollowUp.emit({\n title: this.document().name,\n type: this.document().type,\n placeholder: t(\"askFollowUpPlaceholder\"),\n suggestions: [t(\"suggestionSummarize\"), t(\"suggestionKeyPoints\"), t(\"suggestionActionItems\")]\n });\n }\n}\n","import { afterNextRender, ChangeDetectionStrategy, Component, computed, inject, signal, viewChild } from \"@angular/core\";\nimport { FieldTree, form, FormField, FormRoot, maxLength, required, validate } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n ButtonComponent,\n createCallable,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogResult,\n DialogTitleComponent,\n injectCallRef,\n InputComponent,\n InputControlDirective,\n TagInputComponent,\n TextareaComponent,\n TextareaControlDirective,\n TextareaCounterComponent\n} from \"@sinequa/galactik\";\nimport type { PickedPrincipal } from \"../../models/share.model\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { CurrentUserService } from \"../../services/current-user.service\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { workspaceErrorMessage, WorkspaceErrorMessage } from \"../../utils/error-message\";\nimport { PrincipalPicker } from \"../share/principal-picker\";\n\nexport const TITLE_MAX_LENGTH = 120;\nexport const DESCRIPTION_MAX_LENGTH = 300;\n\nexport type WorkspaceEditDialogConfig = { mode: \"add\" } | { mode: \"edit\"; workspace: Workspace };\n\n/** What the dialog resolves with once it saved: the workspace as the server now has it. */\nexport type WorkspaceEditResult = { type: \"dialog-confirm\"; workspace: Workspace };\n\n/**\n * Creates a workspace, or edits the title, description and tags of one.\n *\n * ```ts\n * const result = await EditWorkspace.call({ mode: \"add\" });\n * if (typeof result === \"object\") open(result[\"workspace\"]);\n * ```\n *\n * It saves by itself — on failure it stays open with the error, so the user can\n * retry — and while a request is in flight it cannot be dismissed. Editing never\n * sends `owners`/`readers`: the API replaces both wholesale, so sending a snapshot\n * taken when the dialog opened could undo what \"Manage access\" did since.\n */\n@Component({\n selector: \"workspace-edit-dialog\",\n imports: [\n TranslocoPipe,\n FormField,\n FormRoot,\n ButtonComponent,\n DialogBodyComponent,\n DialogComponent,\n DialogContentComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n DialogTitleComponent,\n InputComponent,\n InputControlDirective,\n PrincipalPicker,\n TagInputComponent,\n TextareaComponent,\n TextareaControlDirective,\n TextareaCounterComponent\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <dialog #dialog (cancel)=\"onNativeCancel($event)\" (closed)=\"onClosed($event)\">\n <DialogContent size=\"md\">\n <!-- The header's close button cannot be disabled, so the whole header goes inert while the request runs. -->\n <DialogHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\" [attr.inert]=\"busy() ? '' : null\">\n <DialogTitle>{{ (adding() ? \"documentWorkspaces.edit.addTitle\" : \"documentWorkspaces.edit.editTitle\") | transloco }}</DialogTitle>\n </DialogHeader>\n\n <form [formRoot]=\"editForm\" class=\"contents\">\n <DialogBody class=\"gap-(--spacing-md)\">\n <div class=\"flex flex-col gap-(--spacing-2xs)\">\n <label for=\"workspace-edit-name\" class=\"text-sm font-medium\">{{ \"documentWorkspaces.edit.nameLabel\" | transloco }}</label>\n <input-group [scheme]=\"showError(editForm.name) ? 'error' : 'default'\">\n <input\n input-control\n id=\"workspace-edit-name\"\n type=\"text\"\n autocomplete=\"off\"\n autofocus\n [formField]=\"editForm.name\"\n [attr.aria-invalid]=\"showError(editForm.name) ? 'true' : null\" />\n </input-group>\n @if (showError(editForm.name)) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-xs\">{{ \"documentWorkspaces.edit.nameRequired\" | transloco }}</p>\n }\n </div>\n\n <div class=\"flex flex-col gap-(--spacing-2xs)\">\n <label for=\"workspace-edit-description\" class=\"text-sm font-medium\">{{ \"documentWorkspaces.edit.descriptionLabel\" | transloco }}</label>\n <textarea-group [scheme]=\"descriptionTooLong() ? 'error' : 'default'\" resize=\"none\">\n <textarea\n textarea-control\n id=\"workspace-edit-description\"\n rows=\"3\"\n [formField]=\"editForm.description\"\n [attr.placeholder]=\"'documentWorkspaces.edit.descriptionPlaceholder' | transloco\"\n [attr.aria-invalid]=\"descriptionTooLong() ? 'true' : null\"></textarea>\n <textarea-counter [scheme]=\"descriptionTooLong() ? 'error' : 'default'\" data-testid=\"counter\">{{ description().length }}/{{ descriptionMax }}</textarea-counter>\n </textarea-group>\n </div>\n\n <div class=\"flex flex-col gap-(--spacing-2xs)\">\n <span class=\"text-sm font-medium\">{{ \"documentWorkspaces.edit.tagsLabel\" | transloco }}</span>\n <TagInput\n [formField]=\"editForm.tags\"\n [aria-label]=\"'documentWorkspaces.edit.tagsLabel' | transloco\"\n [placeholder]=\"'documentWorkspaces.edit.tagsPlaceholder' | transloco\"\n [addLabel]=\"'documentWorkspaces.edit.addTag' | transloco\"\n [removeLabel]=\"'documentWorkspaces.edit.removeTag' | transloco: { tag: '{tag}' }\"\n [addedLabel]=\"'documentWorkspaces.edit.tagAdded' | transloco: { tag: '{tag}' }\"\n [removedLabel]=\"'documentWorkspaces.edit.tagRemoved' | transloco: { tag: '{tag}' }\" />\n <span class=\"text-(--font-primary-muted) text-xs\">{{ \"documentWorkspaces.edit.tagsHint\" | transloco }}</span>\n </div>\n\n @if (adding()) {\n <!-- Only when creating: an existing workspace has \"Manage access\", and two places writing the same two arrays is one too many. -->\n <div class=\"flex flex-col gap-(--spacing-2xs)\">\n <span class=\"text-sm font-medium\">{{ \"documentWorkspaces.edit.shareLabel\" | transloco }}</span>\n <principal-picker [(entries)]=\"sharedWith\" [showChipRole]=\"true\" [excludeIds]=\"excludedIds()\" />\n <span class=\"text-(--font-primary-muted) text-xs\">{{ \"documentWorkspaces.edit.shareHint\" | transloco }}</span>\n </div>\n }\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">\n @if (\"key\" in message) {\n {{ message.key | transloco }}\n } @else {\n {{ message.text }}\n }\n </p>\n }\n </DialogBody>\n\n <DialogFooter>\n <button type=\"button\" variant=\"secondary\" [disabled]=\"busy()\" (click)=\"dialog.close('dialog-cancel')\">\n {{ \"documentWorkspaces.common.cancel\" | transloco }}\n </button>\n <button type=\"submit\" [disabled]=\"busy() || descriptionTooLong()\">\n {{ saveLabel() | transloco }}\n </button>\n </DialogFooter>\n </form>\n </DialogContent>\n </dialog>\n `\n})\nexport class WorkspaceEditDialog {\n protected readonly call = injectCallRef<WorkspaceEditDialogConfig, DialogResult>();\n\n private readonly dialog = viewChild.required(DialogComponent);\n private readonly store = inject(WorkspacesStore);\n private readonly currentUser = inject(CurrentUserService);\n\n protected readonly titleMax = TITLE_MAX_LENGTH;\n protected readonly descriptionMax = DESCRIPTION_MAX_LENGTH;\n\n private readonly config = this.call.props();\n protected readonly adding = computed(() => this.config.mode === \"add\");\n private readonly initial = this.config.mode === \"edit\" ? this.config.workspace : null;\n\n private readonly model = signal({\n name: this.initial?.name ?? \"\",\n description: this.initial?.description ?? \"\",\n tags: this.initial ? [...this.initial.tags] : []\n });\n private readonly tagInput = viewChild.required(TagInputComponent);\n\n protected readonly editForm = form(\n this.model,\n path => {\n required(path.name);\n // `required` lets whitespace through: a title of spaces is no title.\n validate(path.name, ({ value }) => (value().trim() ? undefined : { kind: \"required\", message: \"required\" }));\n maxLength(path.name, TITLE_MAX_LENGTH);\n maxLength(path.description, DESCRIPTION_MAX_LENGTH);\n },\n { submission: { action: async () => this.save() } }\n );\n\n protected readonly description = computed(() => this.model().description);\n /** The people picked to share a new workspace with. */\n protected readonly sharedWith = signal<PickedPrincipal[]>([]);\n /** Never offered: they are the creator-to-be, and go into `owners` when the workspace is created. */\n protected readonly excludedIds = computed(() => [this.currentUser.user()?.id].filter((id): id is string => !!id));\n protected readonly busy = signal(false);\n protected readonly error = signal<WorkspaceErrorMessage | null>(null);\n\n protected readonly descriptionTooLong = computed(() => this.model().description.length > DESCRIPTION_MAX_LENGTH);\n protected readonly saveLabel = computed(() => {\n if (this.busy()) return this.adding() ? \"documentWorkspaces.edit.creating\" : \"documentWorkspaces.edit.saving\";\n return this.adding() ? \"documentWorkspaces.common.create\" : \"documentWorkspaces.common.save\";\n });\n\n /** What to resolve with once the native dialog has closed: the saved workspace, or the plain dismissal. */\n private outcome: DialogResult | null = null;\n\n constructor() {\n afterNextRender(() => this.dialog().showModal());\n }\n\n /** A field shows its error only once the user has left it, or tried to submit. */\n protected showError<T>(field: FieldTree<T>): boolean {\n return field().touched() && field().invalid();\n }\n\n /** The submission itself, only ever reached with a valid form: the title is there, the description fits. */\n private async save(): Promise<void> {\n if (this.busy()) return;\n\n // What is typed in the tag box and not committed yet still counts.\n this.tagInput().commit();\n\n const { name, description, tags } = this.model();\n this.busy.set(true);\n this.error.set(null);\n try {\n let workspace: Workspace | null;\n if (this.config.mode === \"add\") {\n // The creator goes into `owners` as well as `creator`. Awaited rather than read\n // off the signal: a create fired before the principal request settled would\n // otherwise drop the id silently, and nothing shows the array is short.\n const creator = await this.currentUser.whenReady();\n const shared = this.sharedWith();\n const owners = shared.filter(p => p.role === \"owner\").map(p => p.id);\n workspace = await this.store.create({\n name: name.trim(),\n description: description.trim(),\n tags,\n owners: creator && !owners.includes(creator.id) ? [...owners, creator.id] : owners,\n readers: shared.filter(p => p.role === \"reader\").map(p => p.id)\n });\n } else {\n workspace = await this.store.update(this.config.workspace.id, { name: name.trim(), description: description.trim(), tags });\n }\n if (!workspace) {\n this.error.set({ key: \"documentWorkspaces.errors.notFound\" });\n return;\n }\n this.outcome = { type: \"dialog-confirm\", workspace } satisfies WorkspaceEditResult;\n } catch (error) {\n this.error.set(workspaceErrorMessage(error));\n return;\n } finally {\n this.busy.set(false);\n }\n this.dialog().close(\"dialog-confirm\");\n }\n\n protected onClosed(event: DialogResult): void {\n this.call.end(this.outcome ?? event);\n }\n\n /** Escape on a native <dialog> closes it: refuse while the request is running. */\n protected onNativeCancel(event: Event): void {\n if (this.busy()) event.preventDefault();\n }\n}\n\n/** Imperative entry point: `await EditWorkspace.call({ mode: \"add\" })`. */\nexport const EditWorkspace = createCallable<WorkspaceEditDialogConfig, DialogResult>(WorkspaceEditDialog);\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n effect,\n ElementRef,\n inject,\n Injector,\n input,\n model,\n output,\n signal,\n untracked\n} from \"@angular/core\";\nimport { form, FormField } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe, TranslocoService } from \"@jsverse/transloco\";\nimport { ApiError } from \"@sinequa/atomic\";\nimport {\n ButtonComponent,\n CheckboxComponent,\n ChevronLeftIcon,\n FileSearchIcon,\n GridIcon,\n ListIcon,\n PenToSquareIcon,\n RefreshIcon,\n SearchInputComponent,\n TagIcon,\n TrashIcon,\n UploadIcon,\n UserRoundIcon,\n TooltipDirective\n} from \"@sinequa/galactik\";\nimport { injectLiveRefresh } from \"../../composables/inject-live-refresh\";\nimport { injectSelection } from \"../../composables/inject-selection\";\nimport { ResizeHandle } from \"../../directives/resize-handle.directive\";\nimport { WriterOnly } from \"../../directives/writer-only.directive\";\nimport type { Workspace, WorkspaceDocument } from \"../../models/workspace.model\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { UploadService } from \"../../upload/upload.service\";\nimport { workspaceErrorMessage } from \"../../utils/error-message\";\nimport { Message } from \"../../utils/message\";\nimport { matchesText } from \"../../utils/text-match\";\nimport { canWrite } from \"../../utils/workspace-role\";\nimport { ConfirmAction, ConfirmDialogConfig } from \"../shared/confirm.dialog\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\nimport { UploadDocuments } from \"../upload/upload.dialog\";\nimport { DocumentCard } from \"./document-card\";\nimport { DOCUMENT_ROW_COLUMNS, DocumentRow } from \"./document-row\";\nimport { AskFollowUp, DocumentPreviewPanel } from \"../preview/document-preview-panel\";\nimport { ShareWorkspace } from \"../share/share.dialog\";\nimport { EditWorkspace, WorkspaceEditResult } from \"./workspace-edit.dialog\";\n\ntype ViewMode = \"grid\" | \"list\";\n\n/**\n * One workspace and its documents, as a grid or a list, with search, multi-selection,\n * upload, edit and delete.\n *\n * It reads `WorkspacesStore` for both the workspace and its documents, so a change\n * made elsewhere — a role changed from \"Manage access\", a status patched by the\n * upload flow — shows without anything being pushed in. It does not know where it is\n * shown: it says what the user asked for (`back`, `deleted`, `askFollowUp`) and whoever\n * hosts it decides. The document open in the preview is the `previewedDocumentId` model.\n *\n * Only someone who can write sees the edit, share, delete, upload and select controls.\n *\n * While a workspace stays open, it keeps polling its documents and its own\n * metadata in the background (`injectLiveRefresh`), so an edit or a document\n * another user adds shows up on its own.\n */\n@Component({\n selector: \"workspace-detail\",\n imports: [\n TranslocoPipe,\n FormField,\n ButtonComponent,\n CheckboxComponent,\n ChevronLeftIcon,\n DocumentCard,\n DocumentPreviewPanel,\n DocumentRow,\n FileSearchIcon,\n GridIcon,\n ListIcon,\n PenToSquareIcon,\n RefreshIcon,\n ResizeHandle,\n SearchInputComponent,\n TagIcon,\n TooltipDirective,\n TrashIcon,\n UploadIcon,\n UserRoundIcon,\n WorkspaceMessage,\n WriterOnly\n ],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"flex h-full min-h-0\" },\n template: `\n <div class=\"@container flex min-h-0 min-w-0 flex-1 flex-col p-3 pb-2 md:p-8 md:pb-4\">\n <div class=\"mb-3 flex flex-wrap items-center gap-x-3 gap-y-2\">\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'documentWorkspaces.detail.back' | transloco\"\n [tooltip]=\"'documentWorkspaces.detail.back' | transloco\"\n (click)=\"leave()\">\n <chevron-left-icon />\n </button>\n\n @if (workspace(); as workspace) {\n <h1 class=\"min-w-28 flex-[1_1_16rem] text-2xl leading-[31px] font-bold break-words text-(--font-neutral-black)\">{{ workspace.name }}</h1>\n\n <!-- Beside the title while they fit next to its 16rem, on a line of their own under it once they do not. -->\n @if (!deletedElsewhere()) {\n <div class=\"ml-auto flex shrink-0 items-center gap-1\">\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\"\n [writerOnly]=\"workspace.currentUserRole\"\n (click)=\"confirmDeleteWorkspace(workspace)\">\n <trash-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n <button size=\"sm\" variant=\"tertiary\" scheme=\"neutral\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"share(workspace)\">\n <user-round-icon />\n {{ \"documentWorkspaces.share.title\" | transloco }}\n </button>\n <button size=\"sm\" variant=\"tertiary\" scheme=\"neutral\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"edit(workspace)\">\n <pen-to-square-icon />\n {{ \"documentWorkspaces.common.edit\" | transloco }}\n </button>\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n [iconOnly]=\"true\"\n [disabled]=\"refreshing()\"\n [attr.aria-label]=\"'documentWorkspaces.common.refresh' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.refresh' | transloco\"\n (click)=\"refresh()\">\n <refresh-icon />\n </button>\n </div>\n }\n }\n </div>\n\n @if (workspace(); as workspace) {\n @if (workspace.description) {\n <p class=\"mb-3 text-base text-(--font-primary-base)\">{{ workspace.description }}</p>\n }\n @if (workspace.tags.length) {\n <p class=\"mb-6 flex items-center gap-2 text-sm text-(--font-primary-base)\" data-testid=\"tags\">\n <tag-icon class=\"size-4\" />\n {{ workspace.tags.join(\", \") }}\n </p>\n }\n\n @if (!deletedElsewhere()) {\n <div class=\"mb-3 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <SearchInput\n role=\"search\"\n class=\"min-w-32 flex-[0_1_15rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.detail.searchPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.detail.searchPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n\n <button class=\"@max-md:px-2\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"upload(workspace)\">\n <upload-icon />\n <span class=\"@max-md:sr-only\">{{ \"documentWorkspaces.detail.upload\" | transloco }}</span>\n </button>\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <trash-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n <p class=\"mb-6 text-sm text-(--font-primary-base)\" aria-live=\"polite\" data-testid=\"count\">\n @if (selection.active()) {\n {{ \"documentWorkspaces.detail.selectedDocumentCount\" | transloco: { count: selection.count() } }}\n } @else {\n {{ \"documentWorkspaces.detail.fileCount\" | transloco: { count: workspace.documentCount } }}\n }\n </p>\n }\n }\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">{{ \"documentWorkspaces.detail.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the documents scroll: the title, the toolbar and the count stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-(--font-primary-muted) text-sm\">{{ \"documentWorkspaces.detail.docsLoading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.detail.docsLoadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (deletedElsewhere()) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"not-found\">\n <div class=\"mb-1 inline-flex size-14 shrink-0 items-center justify-center rounded-(--radius-xl) bg-(--teal-100,#E0F4F1) text-(--teal-400,#4EBFB0)\">\n <file-search-icon class=\"size-6\" />\n </div>\n <p class=\"max-w-70 text-sm text-(--font-primary-base)\">{{ \"documentWorkspaces.errors.notFound\" | transloco }}</p>\n <button variant=\"secondary\" size=\"sm\" class=\"mt-2\" (click)=\"leave()\">{{ \"documentWorkspaces.detail.back\" | transloco }}</button>\n </div>\n } @else if (workspace(); as workspace) {\n @if (!documents().length) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"empty\">\n <svg class=\"mb-1 block size-14 shrink-0\" width=\"56\" height=\"56\" viewBox=\"0 0 38 38\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" data-testid=\"empty-icon\">\n <g opacity=\"0.8\">\n <rect class=\"fill-(--teal-100,#E0F4F1)\" width=\"38\" height=\"38\" rx=\"12\" />\n <path\n d=\"M11.2 24.8504H26.8C27.1575 24.8504 27.45 24.5579 27.45 24.2004V14.4504C27.45 14.0929 27.1575 13.8004 26.8 13.8004H20.7347C20.0319 13.8004 19.3453 13.5729 18.7847 13.1504L17.2247 11.9804C17.1109 11.8951 16.9769 11.8504 16.8347 11.8504H11.2C10.8425 11.8504 10.55 12.1429 10.55 12.5004V24.2004C10.55 24.5579 10.8425 24.8504 11.2 24.8504ZM26.8 26.8004H11.2C9.76591 26.8004 8.59998 25.6345 8.59998 24.2004V12.5004C8.59998 11.0663 9.76591 9.90039 11.2 9.90039H16.8347C17.3953 9.90039 17.9437 10.0832 18.3947 10.4204L19.9547 11.5904C20.1781 11.761 20.4544 11.8504 20.7347 11.8504H26.8C28.234 11.8504 29.4 13.0163 29.4 14.4504V24.2004C29.4 25.6345 28.234 26.8004 26.8 26.8004Z\"\n class=\"fill-(--teal-400,#4EBFB0)\" />\n </g>\n </svg>\n <h5 class=\"text-sm font-semibold text-(--font-primary-dark)\">{{ \"documentWorkspaces.detail.emptyTitle\" | transloco }}</h5>\n <p class=\"max-w-70 text-sm text-(--font-primary-base)\">{{ \"documentWorkspaces.detail.emptySubtitle\" | transloco }}</p>\n </div>\n } @else if (!filtered().length) {\n <p class=\"text-(--font-primary-muted) py-8 text-center text-sm\" data-testid=\"no-match\">{{ \"documentWorkspaces.detail.noMatch\" | transloco }}</p>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] content-start gap-4 pb-8\">\n @for (document of filtered(); track document.id) {\n <document-card\n [document]=\"document\"\n [workspaceRole]=\"workspace.currentUserRole\"\n [query]=\"query()\"\n [active]=\"document.id === previewedDocumentId()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(document)\"\n [selectable]=\"selection.canSelect(document)\"\n (opened)=\"togglePreview(document)\"\n (deleteRequested)=\"confirmDeleteDocuments([document])\"\n (selectedChange)=\"selection.toggle(document, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"${DOCUMENT_ROW_COLUMNS} py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <checkbox\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (checkedChange)=\"selection.toggleAll()\" />\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.detail.columnName\" | transloco }}</div>\n <div role=\"columnheader\" class=\"@max-3xl:hidden\">{{ \"documentWorkspaces.detail.columnSource\" | transloco }}</div>\n <div role=\"columnheader\" class=\"@max-3xl:hidden\">{{ \"documentWorkspaces.detail.columnSize\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.detail.columnDate\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (document of filtered(); track document.id) {\n <document-row\n [document]=\"document\"\n [workspaceRole]=\"workspace.currentUserRole\"\n [query]=\"query()\"\n [active]=\"document.id === previewedDocumentId()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(document)\"\n [selectable]=\"selection.canSelect(document)\"\n (opened)=\"togglePreview(document)\"\n (deleteRequested)=\"confirmDeleteDocuments([document])\"\n (selectedChange)=\"selection.toggle(document, $event)\" />\n }\n </div>\n }\n\n @if (hasMore()) {\n <div class=\"flex flex-col items-center gap-2\">\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">{{ \"documentWorkspaces.detail.loadMoreError\" | transloco: { error: message } }}</p>\n }\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.detail.loadMore\" | transloco }}\n </button>\n </div>\n }\n } @else {\n <p class=\"text-(--font-primary-muted) py-8 text-center text-sm\" data-testid=\"not-found\">{{ \"documentWorkspaces.errors.notFound\" | transloco }}</p>\n }\n }\n }\n </div>\n </div>\n\n <!-- The list gives up part of its width to the preview, and takes it back when the preview closes. -->\n <div\n #handle=\"resizeHandle\"\n class=\"group/handle flex w-3 shrink-0 cursor-col-resize items-center justify-center max-md:hidden\"\n [class.hidden]=\"!previewed()\"\n [resizeHandle]=\"previewWidth()\"\n (resizeHandleChange)=\"userPreviewWidth.set($event)\"\n [resizeMin]=\"280\"\n [resizeMax]=\"maxPreviewWidth()\"\n [attr.aria-label]=\"'documentWorkspaces.preview.resize' | transloco\">\n <span class=\"h-6 w-0.5 rounded-full bg-(--stroke-primary-light) opacity-0 group-hover/handle:opacity-100 group-focus-visible/handle:opacity-100 group-data-resizing/handle:opacity-100\"></span>\n </div>\n <aside\n class=\"sticky top-3 my-3 mr-3 flex h-[calc(100dvh-1.5rem)] max-h-[calc(100%-1.5rem)] shrink-0 self-start overflow-hidden rounded-(--radius-xl) bg-(--bg-neutral-white) transition-[width] duration-[350ms] ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none data-open:border data-open:border-(--stroke-primary-lighter) data-open:shadow-[0_4px_6px_-2px_rgba(45,57,58,0.05),0_10px_15px_-3px_rgba(45,57,58,0.1)] max-md:data-open:fixed max-md:data-open:inset-0 max-md:data-open:z-50 max-md:data-open:m-0 max-md:data-open:w-full! max-md:data-open:max-h-none max-md:data-open:rounded-none\"\n [class.transition-none]=\"handle.resizing()\"\n [style.width.px]=\"previewed() ? previewWidth() : 0\"\n [attr.data-open]=\"previewed() ? '' : null\"\n [attr.aria-hidden]=\"previewed() ? null : 'true'\"\n data-testid=\"preview\">\n @if (previewed(); as previewed) {\n <!-- Its own width, so the content does not reflow while the card opens. -->\n <document-preview-panel\n class=\"shrink-0 max-md:w-full!\"\n [style.width.px]=\"previewWidth()\"\n [document]=\"previewed\"\n [queryName]=\"queryName()\"\n (closed)=\"previewedDocumentId.set(null)\"\n (askFollowUp)=\"askFollowUp.emit($event)\" />\n }\n </aside>\n `\n})\nexport class WorkspaceDetail {\n readonly workspaceId = input.required<string>();\n /** The tus endpoint uploads go to — the host's `upload-endpoint`. */\n readonly uploadEndpoint = input(\"\");\n /** The document open in the preview, or `null`. Two-way: the host can open one, and is told when it is opened or closed. */\n readonly previewedDocumentId = model<string | null>(null);\n /** The query the preview web service renders a document for — the host's `query-name`. */\n readonly queryName = input(\"_query\");\n\n /** The user wants to leave for the list. */\n readonly back = output<void>();\n /** The workspace was deleted from here; the host has nothing left to show. */\n readonly deleted = output<Workspace>();\n /** The workspace was edited from here (title, description, tags). */\n readonly workspaceUpdated = output<Workspace>();\n /** \"Ask follow-up\" on a previewed document: the host's chat is what takes it from there. */\n readonly askFollowUp = output<AskFollowUp>();\n\n private readonly store = inject(WorkspacesStore);\n private readonly uploads = inject(UploadService);\n private readonly injector = inject(Injector);\n private readonly transloco = inject(TranslocoService);\n\n protected readonly viewMode = signal<ViewMode>(\"grid\");\n protected readonly query = signal(\"\");\n /** The search box, bound to `query`. */\n protected readonly searchForm = form(this.query);\n protected readonly status = signal<\"loading\" | \"ready\" | \"error\">(\"loading\");\n protected readonly loadError = signal<Message | null>(null);\n protected readonly refreshError = signal<string | null>(null);\n protected readonly refreshing = signal(false);\n protected readonly loadingMore = signal(false);\n protected readonly loadMoreError = signal<string | null>(null);\n /** The background refresh found the workspace gone from the server (deleted elsewhere). */\n protected readonly deletedElsewhere = signal(false);\n\n protected readonly workspace = computed(() => this.store.workspace(this.workspaceId()));\n // The list is the index: only a document the listing returns as indexed is shown. Transfers,\n // indexing and failures belong to the upload tracker, not to the workspace. A document with no\n // status at all is one the server does not track — it is simply there, so it stays listed.\n protected readonly documents = computed(() => this.store.documentsOf(this.workspaceId()).filter(d => !d.status));\n protected readonly previewed = computed(() => this.documents().find(d => d.id === this.previewedDocumentId()) ?? null);\n /** The width the user dragged the preview to, if they did. */\n protected readonly userPreviewWidth = signal<number | null>(null);\n /** The width of the view itself: the preview opens at 40% of it (280px at the least), as in the web element. */\n private readonly hostWidth = signal(0);\n protected readonly previewWidth = computed(() => this.userPreviewWidth() ?? Math.max(280, Math.round(this.hostWidth() * 0.4)));\n /** The list keeps at least 360px. */\n protected readonly maxPreviewWidth = computed(() => (this.hostWidth() ? this.hostWidth() - 360 : undefined));\n protected readonly hasMore = computed(() => this.store.hasMoreDocuments(this.workspaceId()));\n protected readonly filtered = computed(() => {\n const query = this.query().trim();\n return query ? this.documents().filter(d => matchesText(d.name, query)) : this.documents();\n });\n\n private readonly writable = computed(() => canWrite(this.workspace()?.currentUserRole));\n protected readonly selection = injectSelection({\n items: this.filtered,\n key: (document: WorkspaceDocument) => document.id,\n // Selecting only serves deleting, which a reader cannot do.\n selectable: () => this.writable()\n });\n\n // Background polling, re-scoped to whatever `workspaceId` is current on each\n // tick (no restart needed when the user switches workspaces): refreshes both\n // the workspace's own metadata (title, description, tags, owners/readers,\n // the current user's role) and its documents, so an edit or an access change\n // made elsewhere shows up here too, not just additions to the document list.\n // Comparing full snapshots (not just ids) catches an in-place edit that an\n // id-only diff would miss — a renamed workspace, a demoted role — and keeps\n // the backoff from widening on a tick that only looks unchanged by id.\n private readonly liveRefresh = injectLiveRefresh(\n async signal => {\n const id = this.workspaceId();\n const beforeWorkspace = JSON.stringify(this.store.workspace(id));\n const beforeDocuments = JSON.stringify(this.store.documentsOf(id));\n try {\n await Promise.all([this.store.fetch(id), this.store.loadDocuments(id, signal)]);\n } catch (error) {\n // Someone else deleted it: flag it and stop polling — there is nothing\n // left here to discover. The workspace stays in the shared cache (so its\n // name/description keep showing instead of the view going blank); `leave()`\n // drops it once the user actually goes back to the list.\n if (error instanceof ApiError && error.status === 404 && this.workspaceId() === id) {\n this.deletedElsewhere.set(true);\n this.selection.exit();\n this.liveRefresh.stop();\n return true;\n }\n throw error;\n }\n if (this.workspaceId() !== id) return false; // the user moved to another workspace meanwhile\n const documentsChanged = JSON.stringify(this.store.documentsOf(id)) !== beforeDocuments;\n const workspaceChanged = JSON.stringify(this.store.workspace(id)) !== beforeWorkspace;\n // Every tick, changed or not: an upload someone else just started shows no document yet, and the\n // tracker only learns of it from the status API. It stops by itself after a tick when nothing is moving.\n this.uploads.watch(id);\n return documentsChanged || workspaceChanged;\n },\n { intervalMs: 15_000, maxIntervalMs: 90_000 }\n );\n\n constructor() {\n effect(() => {\n this.workspaceId();\n untracked(() => void this.open());\n });\n\n // An access change made elsewhere can demote the user on the workspace they are looking at:\n // leave the selection mode before its entry point goes away, or its bar stays up with no way out.\n effect(() => {\n if (!this.writable()) untracked(() => this.selection.exit());\n });\n\n const host: HTMLElement = inject(ElementRef).nativeElement;\n if (typeof ResizeObserver !== \"undefined\") {\n const observer = new ResizeObserver(([entry]) => this.hostWidth.set(Math.round(entry.contentRect.width)));\n observer.observe(host);\n inject(DestroyRef).onDestroy(() => observer.disconnect());\n }\n\n inject(DestroyRef).onDestroy(() => this.uploads.watch(null));\n }\n\n private async open(): Promise<void> {\n this.selection.exit();\n this.previewedDocumentId.set(null);\n this.query.set(\"\");\n this.refreshError.set(null);\n this.loadMoreError.set(null);\n this.deletedElsewhere.set(false);\n await this.load();\n }\n\n /**\n * Leaving from here: once the background refresh found the workspace gone,\n * the cache still holds it (so its name kept showing) — drop it now, so a\n * stale card does not show it back on the list.\n */\n protected leave(): void {\n if (this.deletedElsewhere()) this.store.forget(this.workspaceId());\n this.back.emit();\n }\n\n /** Reads the workspace (when it is not cached) and its documents, for the workspace currently shown. */\n protected async load(): Promise<void> {\n const id = this.workspaceId();\n this.loadError.set(null);\n this.status.set(\"loading\");\n try {\n if (!this.store.workspace(id)) await this.store.fetch(id);\n await this.store.loadDocuments(id);\n } catch (error) {\n // The user may have moved to another workspace meanwhile: do not clobber what is shown now.\n if (this.workspaceId() !== id) return;\n this.loadError.set(workspaceErrorMessage(error));\n this.status.set(\"error\");\n return;\n }\n if (this.workspaceId() !== id) return;\n this.status.set(\"ready\");\n this.uploads.watch(id);\n this.liveRefresh.start();\n }\n\n /** Reads the documents again without leaving the view; a failure leaves the ones shown as they are. */\n protected async refresh(): Promise<void> {\n if (this.refreshing()) return;\n const id = this.workspaceId();\n this.refreshError.set(null);\n this.refreshing.set(true);\n try {\n await this.store.loadDocuments(id);\n if (this.workspaceId() === id) this.uploads.watch(id);\n } catch (error) {\n if (this.workspaceId() === id) this.refreshError.set(this.translate(workspaceErrorMessage(error)));\n } finally {\n this.refreshing.set(false);\n }\n }\n\n protected async loadMore(): Promise<void> {\n if (this.loadingMore()) return;\n const id = this.workspaceId();\n this.loadMoreError.set(null);\n this.loadingMore.set(true);\n try {\n await this.store.loadMoreDocuments(id);\n } catch (error) {\n if (this.workspaceId() === id) this.loadMoreError.set(this.translate(workspaceErrorMessage(error)));\n } finally {\n this.loadingMore.set(false);\n }\n }\n\n /** Opening the open one again closes it. */\n protected togglePreview(document: WorkspaceDocument): void {\n this.previewedDocumentId.update(id => (id === document.id ? null : document.id));\n }\n\n protected upload(workspace: Workspace): void {\n void UploadDocuments.call({ workspace, endpoint: this.uploadEndpoint() }, { injector: this.injector });\n }\n\n protected share(workspace: Workspace): void {\n void ShareWorkspace.call({ workspaceId: workspace.id }, { injector: this.injector });\n }\n\n protected async edit(workspace: Workspace): Promise<void> {\n const result = await EditWorkspace.call({ mode: \"edit\", workspace }, { injector: this.injector });\n if (typeof result === \"object\") this.workspaceUpdated.emit((result as WorkspaceEditResult).workspace);\n }\n\n protected async confirmDeleteWorkspace(workspace: Workspace): Promise<void> {\n const config: ConfirmDialogConfig = {\n title: this.transloco.translate(\"documentWorkspaces.list.deleteTitle\"),\n description: this.transloco.translate(\"documentWorkspaces.common.deleteWarning\", { name: workspace.name }),\n emphasis: workspace.name,\n destructive: true,\n action: async () => {\n const { failed } = await this.store.remove([workspace.id]);\n if (failed.length) throw new Error(failed[0].error);\n }\n };\n const result = await ConfirmAction.call(config, { injector: this.injector });\n if (result === \"dialog-confirm\") this.deleted.emit(workspace);\n }\n\n protected deleteSelected(): void {\n void this.confirmDeleteDocuments(this.selection.selectedItems());\n }\n\n protected async confirmDeleteDocuments(targets: WorkspaceDocument[]): Promise<void> {\n if (!targets.length) return;\n const id = this.workspaceId();\n const ids = targets.map(d => d.id);\n const single = targets.length === 1;\n const subject = single ? targets[0].name : this.transloco.translate(\"documentWorkspaces.detail.documentCount\", { count: targets.length });\n\n const config: ConfirmDialogConfig = {\n title: single\n ? this.transloco.translate(\"documentWorkspaces.detail.deleteDocTitle\")\n : this.transloco.translate(\"documentWorkspaces.detail.deleteDocumentsTitle\", { count: targets.length }),\n description: this.transloco.translate(\"documentWorkspaces.common.deleteWarning\", { name: subject }),\n emphasis: subject,\n destructive: true,\n action: () => this.store.removeDocuments(id, ids)\n };\n const result = await ConfirmAction.call(config, { injector: this.injector });\n if (result === \"dialog-confirm\") this.selection.exit();\n }\n\n private translate(message: Message): string {\n return \"key\" in message ? this.transloco.translate(message.key, message.params) : message.text;\n }\n}\n","import { assertInInjectionContext, computed, effect, inject, InjectionToken, Injector, signal, untracked } from \"@angular/core\";\nimport { EditWorkspace, WorkspaceEditResult } from \"../components/detail/workspace-edit.dialog\";\nimport type { Workspace, WorkspaceColor, WorkspaceRole } from \"../models/workspace.model\";\nimport { WorkspacesStore } from \"../store/workspaces.store\";\nimport { injectWorkspaceFilter, WorkspaceSearchFields, WorkspaceTab } from \"./inject-workspace-filter\";\n\n/** What a host chat reads to build its own mention menu. */\nexport interface MentionSelection {\n ids: string[];\n count: number;\n}\n\n/** One displayed workspace, with whether it is ticked. */\nexport interface MentionItem {\n id: string;\n name: string;\n description: string;\n color: WorkspaceColor;\n documentCount: number;\n /** The current user's role on it: a host can mark the read-only rows (`reader`) without hiding them. */\n currentUserRole: WorkspaceRole;\n selected: boolean;\n}\n\n/** The categories, in order: the translation key of each one's label. */\nexport const MENTION_TABS: readonly { id: WorkspaceTab; labelKey: string }[] = [\n { id: \"all\", labelKey: \"documentWorkspaces.mention.tabAll\" },\n { id: \"editor\", labelKey: \"documentWorkspaces.mention.tabOwned\" },\n { id: \"reader\", labelKey: \"documentWorkspaces.mention.tabShared\" }\n];\n\nexport interface WorkspaceMentionOptions {\n /** What the search matches. Read reactively; the name only when absent. */\n searchFields?: () => WorkspaceSearchFields;\n /**\n * The ticked ids, when the host owns the selection (controlled mode): the ticks are then\n * the host's and the store's are left alone. `undefined` — the default — keeps them in the store.\n */\n selectedIds?: () => readonly string[] | undefined;\n /** An extra condition a workspace must meet to be offered, on top of the tab and the search. */\n predicate?: () => ((workspace: Workspace) => boolean) | undefined;\n /** The ticks changed because of this menu. */\n onSelectionChange?: (selection: MentionSelection) => void;\n /** A workspace was created from here, and ticked. */\n onCreated?: (workspace: Workspace) => void;\n}\n\n/**\n * The state behind a \"mention a workspace\" menu — search, category, ticks, \"select all\"\n * and \"new workspace\" — with no markup, so any layout can be drawn over it.\n *\n * The ticks are the store's unless the host passes `selectedIds`: however many menus\n * are on screen they then show the same thing. \"Select all\" works on what is displayed,\n * so on the current category and search. Every workspace is offered, the ones shared\n * read-only too — mentioning one only reads it.\n *\n * Must be called from an injection context (a component field initializer).\n */\nexport function injectWorkspaceMention(options: WorkspaceMentionOptions = {}) {\n assertInInjectionContext(injectWorkspaceMention);\n\n const store = inject(WorkspacesStore);\n const injector = inject(Injector);\n const filter = injectWorkspaceFilter(store.workspaces, { fields: options.searchFields });\n\n const loadStatus = signal<\"loading\" | \"ready\" | \"error\">(store.loaded() ? \"ready\" : \"loading\");\n if (!store.loaded()) {\n // Another list or picker may be the one that loads: the store is shared, and this follows it.\n store.ensureLoaded().then(\n () => loadStatus.set(\"ready\"),\n () => loadStatus.set(\"error\")\n );\n }\n\n const loadingMore = signal(false);\n\n /** The ids the host passed in controlled mode — possibly of workspaces deleted since. */\n const hostIds = computed(() => options.selectedIds?.());\n // The store drops a deleted workspace from its own ticks, but a host's ticks are its own: in\n // controlled mode the ids are cut down to the workspaces that exist. Only once the list is\n // complete — a workspace past the loaded pages is not a deleted one.\n const listComplete = computed(() => store.loaded() && !store.hasMore() && !store.truncated());\n const selectedIds = computed<readonly string[]>(() => {\n const host = hostIds();\n if (!host) return store.selectedIds();\n if (!listComplete()) return host;\n const known = new Set(store.workspaces().map(w => w.id));\n return host.filter(id => known.has(id));\n });\n\n effect(() => {\n const host = hostIds();\n const kept = selectedIds();\n if (!host || host.length === kept.length) return;\n // The host hears about it like any other change; `untracked` keeps its callback's reads out of this effect.\n untracked(() => options.onSelectionChange?.({ ids: [...kept], count: kept.length }));\n });\n const selectedSet = computed(() => new Set(selectedIds()));\n\n const items = computed<MentionItem[]>(() => {\n const only = options.predicate?.();\n return filter\n .filtered()\n .filter(w => !only || only(w))\n .map(w => ({\n id: w.id,\n name: w.name,\n description: w.description,\n color: w.color,\n documentCount: w.documentCount,\n currentUserRole: w.currentUserRole,\n selected: selectedSet().has(w.id)\n }));\n });\n\n const count = computed(() => selectedIds().length);\n const ticked = computed(() => items().filter(item => item.selected).length);\n // `length > 0` matters: [].every() is true, which would label an empty list \"Unselect all\".\n const allSelected = computed(() => items().length > 0 && ticked() === items().length);\n const partiallySelected = computed(() => ticked() > 0 && !allSelected());\n\n /** Writes the ticks where they live, and says so. */\n const commit = (ids: string[]) => {\n if (!options.selectedIds?.()) {\n const next = new Set(ids);\n for (const id of selectedSet()) if (!next.has(id)) store.toggleSelected(id, false);\n for (const id of next) if (!selectedSet().has(id)) store.toggleSelected(id, true);\n }\n options.onSelectionChange?.({ ids: [...ids], count: ids.length });\n };\n\n const toggle = (id: string, checked: boolean) => {\n const without = selectedIds().filter(selected => selected !== id);\n commit(checked ? [...without, id] : without);\n };\n\n return {\n /** The search text; writable, so a text box can bind to it. */\n query: filter.query,\n clearQuery: filter.clearQuery,\n /** The category: all, the ones you own (`editor`), the ones shared with you (`reader`). */\n tab: filter.tab,\n tabs: MENTION_TABS,\n /** The workspaces in each category — the whole list, not what the search left. */\n counts: filter.counts,\n /** Whether the first page is loading, ready or failed. */\n status: loadStatus.asReadonly(),\n /** More workspaces than the loaded ones exist on the server: `loadMore()` fetches the next page. */\n hasMore: store.hasMore,\n /** The loading cap is reached: some workspaces will never be offered, search included. */\n truncated: store.truncated,\n /** A page is being fetched by `loadMore()`. */\n loadingMore: loadingMore.asReadonly(),\n /** Loads the next page; the search and the categories then run over it too. */\n async loadMore(): Promise<void> {\n loadingMore.set(true);\n try {\n await store.loadMore();\n } finally {\n loadingMore.set(false);\n }\n },\n /** What is offered: the category, the search and the predicate applied. */\n items,\n /** How many are ticked, displayed or not. */\n count,\n /** The ticked ids, displayed or not. */\n ids: selectedIds,\n allSelected,\n partiallySelected,\n isSelected: (id: string) => selectedSet().has(id),\n toggle,\n /** Ticks every displayed workspace, or unticks them all when they already are. */\n toggleAll: () => {\n const displayed = items().map(item => item.id);\n if (allSelected()) {\n const drop = new Set(displayed);\n commit(selectedIds().filter(id => !drop.has(id)));\n } else {\n commit([...new Set([...selectedIds(), ...displayed])]);\n }\n },\n /** Ticks a workspace from outside — a host that has its own way of choosing one. */\n select: (id: string) => toggle(id, true),\n /**\n * Opens the \"new workspace\" dialog; the workspace it makes is ticked and shown (the category\n * and the search are reset so it is not hidden). Resolves with it, or `null` when dismissed.\n *\n * The dialog is deliberately not tied to the caller's `DestroyRef`: a menu that closes on\n * focus-out (a hover-mounted panel) would end it mid-save.\n */\n async create(): Promise<Workspace | null> {\n const result = await EditWorkspace.call({ mode: \"add\" }, { injector });\n if (typeof result !== \"object\") return null;\n const { workspace } = result as WorkspaceEditResult;\n filter.tab.set(\"all\");\n filter.clearQuery();\n toggle(workspace.id, true);\n options.onCreated?.(workspace);\n return workspace;\n }\n };\n}\n\nexport type WorkspaceMentionApi = ReturnType<typeof injectWorkspaceMention>;\n\n/** Provided by `<workspace-mention>` to the pieces placed inside it. */\nexport const WORKSPACE_MENTION = new InjectionToken<WorkspaceMentionApi>(\"WORKSPACE_MENTION\");\n\n/** The enclosing `<workspace-mention>`, for a piece that cannot work outside one. */\nexport function injectWorkspaceMentionScope(): WorkspaceMentionApi {\n const scope = inject(WORKSPACE_MENTION, { optional: true });\n if (!scope) throw new Error(\"[atomic] This piece must be placed inside a <workspace-mention>.\");\n return scope;\n}\n","/**\n * Whether the host put something of its own in a slot: an element, or text that is not blank.\n * Comments and whitespace — what an empty `@if` or a line break leaves behind — do not count.\n */\nexport function hasContent(slot: HTMLElement): boolean {\n return Array.from(slot.childNodes).some(node => node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && !!node.textContent?.trim()));\n}\n","import { AfterContentInit, ChangeDetectionStrategy, Component, ElementRef, input, signal, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, type ButtonVariants, PlusIcon } from \"@sinequa/galactik\";\nimport { hasContent } from \"./has-content\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\n\n/**\n * The \"+ New workspace\" button of the enclosing `<workspace-mention>`: it opens the same\n * dialog as the workspaces page, and the workspace it makes is ticked.\n *\n * Its look is the host's: `variant`, `scheme` and `size` are the button's own, and content\n * placed inside replaces the icon and the label.\n *\n * ```html\n * <workspace-mention-new variant=\"primary\" size=\"md\">Add a workspace</workspace-mention-new>\n * ```\n */\n@Component({\n selector: \"workspace-mention-new\",\n imports: [TranslocoPipe, ButtonComponent, PlusIcon],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"inline-block\" },\n template: `\n <button type=\"button\" [variant]=\"variant()\" [scheme]=\"scheme()\" [size]=\"size()\" (click)=\"create()\">\n <span #slot class=\"contents\"><ng-content /></span>\n @if (!custom()) {\n <plus-icon />\n {{ \"documentWorkspaces.mention.newWorkspace\" | transloco }}\n }\n </button>\n `\n})\nexport class WorkspaceMentionNew implements AfterContentInit {\n /** The button's variant. */\n readonly variant = input<ButtonVariants[\"variant\"]>(\"tertiary\");\n /** The button's colour scheme. */\n readonly scheme = input<ButtonVariants[\"scheme\"]>(\"neutral\");\n /** The button's size. */\n readonly size = input<ButtonVariants[\"size\"]>(\"sm\");\n\n private readonly mention = injectWorkspaceMentionScope();\n private readonly slot = viewChild.required<ElementRef<HTMLElement>>(\"slot\");\n /** Whether the host put its own content inside: the icon and the label then stay out. */\n protected readonly custom = signal(false);\n\n ngAfterContentInit(): void {\n this.custom.set(hasContent(this.slot().nativeElement));\n }\n\n protected create(): void {\n void this.mention.create();\n }\n}\n","import { ChangeDetectionStrategy, Component } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { CheckboxComponent } from \"@sinequa/galactik\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\nimport { WorkspaceIcon } from \"../shared/workspace-icon\";\n\n/**\n * The workspaces of the enclosing `<workspace-mention>` as rows to tick: the default\n * look of the menu. A host that wants another look loops over the scope's `items()` itself.\n *\n * It fills the height its host gives it and scrolls inside it; with no fixed height around it,\n * it stops at 18rem — or at `--workspace-mention-rows-max-height`, which the host can set (`none` lifts it).\n */\n@Component({\n selector: \"workspace-mention-rows\",\n imports: [TranslocoPipe, CheckboxComponent, WorkspaceIcon],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block min-h-0 max-h-(--workspace-mention-rows-max-height,18rem) flex-1 overflow-auto\" },\n template: `\n @switch (mention.status()) {\n @case (\"loading\") {\n <p class=\"px-(--spacing-xs) py-(--spacing-2xs) text-(length:--size-base) text-(--font-primary-muted)\">{{ \"documentWorkspaces.mention.loading\" | transloco }}</p>\n }\n @case (\"error\") {\n <p class=\"px-(--spacing-xs) py-(--spacing-2xs) text-(length:--size-base) text-(--font-error-base)\" role=\"alert\">{{ \"documentWorkspaces.mention.loadError\" | transloco }}</p>\n }\n @default {\n @if (!mention.items().length) {\n @if (mention.query()) {\n <p class=\"px-(--spacing-xs) py-(--spacing-2xs) text-(length:--size-base) text-(--font-primary-muted)\" data-testid=\"no-match\">{{ \"documentWorkspaces.mention.noMatch\" | transloco }}</p>\n } @else {\n <p class=\"px-(--spacing-xs) py-(--spacing-2xs) text-(length:--size-base) text-(--font-primary-muted)\" data-testid=\"empty\">{{ \"documentWorkspaces.mention.empty\" | transloco }}</p>\n }\n } @else {\n <!-- The checkbox carries the semantics (a checkbox in a list): no listbox/option around it. -->\n <ul class=\"flex flex-col\">\n @for (item of mention.items(); track item.id) {\n <li class=\"rounded-(--radius-sm) hover:bg-(--bg-primary-lightest) data-selected:bg-(--bg-primary-lighter)\" [attr.data-selected]=\"item.selected ? '' : null\">\n <checkbox class=\"w-full items-center gap-(--spacing-xs) px-(--spacing-xs) py-(--spacing-2xs)\" [checked]=\"item.selected\" (checkedChange)=\"mention.toggle(item.id, $event)\">\n <span class=\"flex items-center gap-(--spacing-xs)\">\n <workspace-icon size=\"md\" [color]=\"item.color\" />\n <span class=\"flex min-w-0 flex-col\">\n <span class=\"truncate text-(length:--size-base) font-semibold text-(--font-neutral-black)\">{{ item.name }}</span>\n <span class=\"text-(length:--size-sm) text-(--font-primary-muted)\">{{ \"documentWorkspaces.mention.documentCount\" | transloco: { count: item.documentCount } }}</span>\n </span>\n </span>\n </checkbox>\n </li>\n }\n </ul>\n }\n }\n }\n `\n})\nexport class WorkspaceMentionRows {\n protected readonly mention = injectWorkspaceMentionScope();\n}\n","import { ChangeDetectionStrategy, Component, input } from \"@angular/core\";\nimport { form, FormField } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { type InputWrapVariants, SearchInputComponent } from \"@sinequa/galactik\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\n\n/** The search box of the enclosing `<workspace-mention>`: it narrows what the menu offers. */\n@Component({\n selector: \"workspace-mention-search\",\n imports: [TranslocoPipe, FormField, SearchInputComponent],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block\" },\n template: `\n <SearchInput\n class=\"w-full\"\n [size]=\"size()\"\n [textSize]=\"textSize()\"\n [formField]=\"searchForm\"\n [placeholder]=\"placeholder() ?? ('documentWorkspaces.mention.searchPlaceholder' | transloco)\"\n [aria-label]=\"ariaLabel() ?? placeholder() ?? ('documentWorkspaces.mention.searchPlaceholder' | transloco)\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n `\n})\nexport class WorkspaceMentionSearch {\n /** The size of the box, as `<SearchInput>` sizes it. */\n readonly size = input<InputWrapVariants[\"size\"]>(\"md\");\n /** The size of the text, as `<SearchInput>` sizes it; follows `size` when absent. */\n readonly textSize = input<InputWrapVariants[\"textSize\"]>();\n /** The hint shown in the empty box; the translated \"Search workspace...\" when absent. */\n readonly placeholder = input<string>();\n /** The accessible name of the box; the placeholder when absent. */\n readonly ariaLabel = input<string | undefined>(undefined, { alias: \"aria-label\" });\n\n private readonly mention = injectWorkspaceMentionScope();\n\n /** Bound to the scope's own query signal. */\n protected readonly searchForm = form(this.mention.query);\n}\n","import { ChangeDetectionStrategy, Component } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { CheckboxComponent } from \"@sinequa/galactik\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\n\n/**\n * The \"select all\" checkbox of the enclosing `<workspace-mention>`. It acts on what is\n * displayed — the current category and search — and shows a mixed state when only some are ticked.\n * Absent while the list loads and while nothing is displayed: there is nothing to select.\n */\n@Component({\n selector: \"workspace-mention-select-all\",\n imports: [TranslocoPipe, CheckboxComponent],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block\" },\n template: `\n @if (mention.status() === \"ready\" && mention.items().length) {\n <checkbox class=\"items-center gap-(--spacing-xs)\" [checked]=\"mention.allSelected()\" [indeterminate]=\"mention.partiallySelected()\" (checkedChange)=\"mention.toggleAll()\">\n <span class=\"text-(length:--size-base) text-(--font-neutral-black)\">{{ (mention.allSelected() ? \"documentWorkspaces.mention.unselectAll\" : \"documentWorkspaces.mention.selectAll\") | transloco }}</span>\n </checkbox>\n }\n `\n})\nexport class WorkspaceMentionSelectAll {\n protected readonly mention = injectWorkspaceMentionScope();\n}\n","import {\n AfterContentInit,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n forwardRef,\n input,\n output,\n signal,\n viewChild\n} from \"@angular/core\";\nimport type { WorkspaceSearchFields } from \"../../composables/inject-workspace-filter\";\nimport { injectWorkspaceMention, MentionSelection, WORKSPACE_MENTION, WorkspaceMentionApi } from \"../../composables/inject-workspace-mention\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { hasContent } from \"./has-content\";\nimport { WorkspaceMentionNew } from \"./workspace-mention-new\";\nimport { WorkspaceMentionRows } from \"./workspace-mention-rows\";\nimport { WorkspaceMentionSearch } from \"./workspace-mention-search\";\nimport { WorkspaceMentionSelectAll } from \"./workspace-mention-select-all\";\n\n/**\n * Mention workspaces: the state of the menu (search, category, ticks, \"select all\", \"new\n * workspace\") shared with the pieces placed inside it.\n *\n * Empty, it is the ready-made menu — the rows, plus the search box, \"select all\" and \"new\n * workspace\" you switch on:\n *\n * ```html\n * <workspace-mention searchable selectAll allowCreate (selectionChanged)=\"onMention($event)\" />\n * ```\n *\n * With content, it only provides the state, and you lay it out with the pieces\n * (`workspace-mention-search`, `-select-all`, `-new`, `-tabs`) and your own markup over `items()`:\n *\n * ```html\n * <workspace-mention #ws=\"workspaceMention\" searchFields=\"name+description\" [selectedIds]=\"ids()\" (selectionChanged)=\"ids.set($event.ids)\">\n * <workspace-mention-search />\n * @for (w of ws.items(); track w.id) { <my-card [workspace]=\"w\" (click)=\"ws.toggle(w.id, !w.selected)\" /> }\n * </workspace-mention>\n * ```\n *\n * The ticks are the store's, so every menu on screen shows the same ones — unless `selectedIds`\n * is passed: the ticks are then the host's, and it hears each change from `selectionChanged`.\n */\n@Component({\n selector: \"workspace-mention\",\n exportAs: \"workspaceMention\",\n imports: [WorkspaceMentionNew, WorkspaceMentionRows, WorkspaceMentionSearch, WorkspaceMentionSelectAll],\n providers: [{ provide: WORKSPACE_MENTION, useExisting: forwardRef(() => WorkspaceMention) }],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"contents\" },\n template: `\n <div #slot class=\"contents\"><ng-content /></div>\n @if (!custom()) {\n <div class=\"flex min-h-0 flex-1 flex-col gap-(--spacing-2xs)\">\n @if (searchable() || allowCreate()) {\n <div class=\"flex items-center gap-(--spacing-2xs) px-(--spacing-xs) pt-(--spacing-2xs)\">\n @if (searchable()) {\n <workspace-mention-search class=\"min-w-0 flex-1\" />\n }\n @if (allowCreate()) {\n <workspace-mention-new class=\"shrink-0\" />\n }\n </div>\n }\n @if (selectAll()) {\n <workspace-mention-select-all class=\"px-(--spacing-xs)\" />\n }\n <workspace-mention-rows />\n </div>\n }\n `\n})\nexport class WorkspaceMention implements WorkspaceMentionApi, AfterContentInit {\n /** What the search matches: the name only (default), or the name and the description. */\n readonly searchFields = input<WorkspaceSearchFields>(\"name\");\n /** The ticked ids when the host owns the selection; absent, they live in the store. */\n readonly selectedIds = input<readonly string[] | undefined>(undefined);\n /** An extra condition a workspace must meet to be offered, on top of the category and the search. */\n readonly predicate = input<((workspace: Workspace) => boolean) | undefined>(undefined);\n /** The ready-made menu only: shows the search box. */\n readonly searchable = input(false, { transform: booleanAttribute });\n /** The ready-made menu only: shows \"select all\". */\n readonly selectAll = input(false, { transform: booleanAttribute });\n /** The ready-made menu only: shows \"new workspace\". */\n readonly allowCreate = input(false, { transform: booleanAttribute });\n\n /** The ticks changed because of this menu. */\n readonly selectionChanged = output<MentionSelection>();\n /** A workspace was created from here, and ticked. */\n readonly workspaceCreated = output<Workspace>();\n\n private readonly slot = viewChild.required<ElementRef<HTMLElement>>(\"slot\");\n /** Whether the host put its own content inside: the ready-made menu then stays out. */\n protected readonly custom = signal(false);\n\n private readonly state = injectWorkspaceMention({\n searchFields: () => this.searchFields(),\n selectedIds: () => this.selectedIds(),\n predicate: () => this.predicate(),\n onSelectionChange: selection => this.selectionChanged.emit(selection),\n onCreated: workspace => this.workspaceCreated.emit(workspace)\n });\n\n // The state, as the pieces and a template reference (`#ws=\"workspaceMention\"`) read it.\n readonly query = this.state.query;\n readonly clearQuery = this.state.clearQuery;\n readonly tab = this.state.tab;\n readonly tabs = this.state.tabs;\n readonly counts = this.state.counts;\n readonly status = this.state.status;\n readonly hasMore = this.state.hasMore;\n readonly truncated = this.state.truncated;\n readonly loadingMore = this.state.loadingMore;\n readonly loadMore = this.state.loadMore;\n readonly items = this.state.items;\n readonly count = this.state.count;\n readonly ids = this.state.ids;\n readonly allSelected = this.state.allSelected;\n readonly partiallySelected = this.state.partiallySelected;\n readonly isSelected = this.state.isSelected;\n readonly toggle = this.state.toggle;\n readonly toggleAll = this.state.toggleAll;\n readonly select = this.state.select;\n readonly create = this.state.create;\n\n ngAfterContentInit(): void {\n // The projected nodes are in place before the view is first drawn: no flash of the default.\n this.custom.set(hasContent(this.slot().nativeElement));\n }\n}\n","import { booleanAttribute, ChangeDetectionStrategy, Component, computed, input, output, viewChild } from \"@angular/core\";\nimport type { WorkspaceSearchFields } from \"../../composables/inject-workspace-filter\";\nimport type { MentionSelection } from \"../../composables/inject-workspace-mention\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { WorkspaceMention } from \"./workspace-mention\";\n\nexport type { MentionSelection } from \"../../composables/inject-workspace-mention\";\n\n/**\n * The workspaces as rows to tick, for the \"+\" / \"@\" menu of a host chat — the ready-made\n * `<workspace-mention>`, with the search box, \"select all\" and \"new workspace\" switched on\n * by `searchable`, `selectAll` and `allowCreate` (all off by default).\n *\n * The ticks are the store's, not this component's, unless `selectedIds` is passed: however\n * many of these are on screen (one per open menu), and wherever else the selection is read,\n * they then show the same thing. Every workspace is listed, the ones shared read-only too —\n * mentioning one only reads it.\n *\n * To lay the pieces out yourself, use `<workspace-mention>` with its own content instead.\n */\n@Component({\n selector: \"workspace-mention-list\",\n imports: [WorkspaceMention],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block\" },\n template: `\n <workspace-mention\n [searchable]=\"searchable()\"\n [selectAll]=\"selectAll()\"\n [allowCreate]=\"allowCreate()\"\n [searchFields]=\"searchFields()\"\n [selectedIds]=\"selectedIds()\"\n (selectionChanged)=\"selectionChanged.emit($event)\"\n (workspaceCreated)=\"workspaceCreated.emit($event)\" />\n `\n})\nexport class WorkspaceMentionList {\n /** Shows a search box. */\n readonly searchable = input(false, { transform: booleanAttribute });\n /** Shows \"select all\". */\n readonly selectAll = input(false, { transform: booleanAttribute });\n /** Shows \"new workspace\". */\n readonly allowCreate = input(false, { transform: booleanAttribute });\n /** What the search matches: the name only (default), or the name and the description. */\n readonly searchFields = input<WorkspaceSearchFields>(\"name\");\n /** The ticked ids when the host owns the selection; absent, they live in the store. */\n readonly selectedIds = input<readonly string[] | undefined>(undefined);\n\n /** The ticks changed because of this list. */\n readonly selectionChanged = output<MentionSelection>();\n /** A workspace was created from here, and ticked. */\n readonly workspaceCreated = output<Workspace>();\n\n private readonly mention = viewChild.required(WorkspaceMention);\n\n /** The rows, for this list and for a host that draws its own. */\n readonly items = computed(() => this.mention().items());\n\n /** Ticks a workspace from outside — a host that has its own way of choosing one. */\n select(id: string): void {\n this.mention().select(id);\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { BadgeComponent, TabComponent, TabListComponent, TabsComponent } from \"@sinequa/galactik\";\nimport { injectWorkspaceMentionScope } from \"../../composables/inject-workspace-mention\";\nimport type { WorkspaceTab } from \"../../composables/inject-workspace-filter\";\n\n/**\n * The categories of the enclosing `<workspace-mention>` — all, owned, shared — each with the\n * number of workspaces it holds. A host that lays the categories out differently (a sidebar)\n * reads `tabs`, `tab` and `counts` from the scope instead.\n */\n@Component({\n selector: \"workspace-mention-tabs\",\n imports: [TranslocoPipe, BadgeComponent, TabComponent, TabListComponent, TabsComponent],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block max-w-full\" },\n template: `\n <Tabs class=\"max-w-full\">\n <TabList\n variant=\"inner\"\n class=\"w-fit max-w-full\"\n selectionMode=\"explicit\"\n [softDisabled]=\"false\"\n [attr.aria-label]=\"ariaLabel() ?? ('documentWorkspaces.mention.tabsLabel' | transloco)\"\n [selectedTab]=\"mention.tab()\"\n (selectedTabChange)=\"onTabChange($event)\">\n @for (item of mention.tabs; track item.id) {\n <Tab [value]=\"item.id\" variant=\"inner\">\n <span>{{ item.labelKey | transloco }}</span>\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"shrink-0\" [attr.data-testid]=\"'badge-' + item.id\">{{ mention.counts()[item.id] }}</Badge>\n </Tab>\n }\n </TabList>\n </Tabs>\n `\n})\nexport class WorkspaceMentionTabs {\n /** The accessible name of the category list; the translated \"Categories\" when absent. */\n readonly ariaLabel = input<string | undefined>(undefined, { alias: \"aria-label\" });\n\n protected readonly mention = injectWorkspaceMentionScope();\n\n protected onTabChange(tab: string | undefined): void {\n if (tab) this.mention.tab.set(tab as WorkspaceTab);\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input, output, signal } from \"@angular/core\";\nimport { form, FormField, maxLength } from \"@angular/forms/signals\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport { ButtonComponent, CheckIcon, InputComponent, InputControlDirective, PlusIcon, XMarkIcon } from \"@sinequa/galactik\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { WorkspacesStore } from \"../../store/workspaces.store\";\nimport { workspaceErrorMessage } from \"../../utils/error-message\";\nimport type { Message } from \"../../utils/message\";\nimport { canWrite } from \"../../utils/workspace-role\";\nimport { WorkspaceIcon } from \"../shared/workspace-icon\";\nimport { WorkspaceMessage } from \"../shared/workspace-message\";\n\n/** What \"save\" attaches to a workspace: a document that already exists, by name. */\nexport interface PickerDocument {\n name: string;\n}\n\nexport const NEW_WORKSPACE_NAME_MAX = 50;\n\n/** How long the check mark stays before the picker says it is done. */\nconst SAVE_FEEDBACK_MS = 900;\nconst MOVE_FEEDBACK_MS = 700;\n\n/**\n * Picks the workspace to put documents in — \"Save to workspace\", or \"Move to\n * workspace\" when they come from another one — and can make a new one on the spot.\n *\n * Only the workspaces the user can write to are offered: attaching a document is a\n * write, so a workspace shared read-only stays out (it stays visible wherever it is\n * merely read). When moving, the workspace the documents come from is left out too.\n *\n * It does the work (the store's `saveDocuments` / `moveDocuments`) and says when it is\n * over — `completed` — once the check mark has been seen; whoever shows it (a popover,\n * a bottom sheet) decides what closing means.\n */\n@Component({\n selector: \"workspace-picker\",\n imports: [TranslocoPipe, FormField, ButtonComponent, CheckIcon, InputComponent, InputControlDirective, PlusIcon, WorkspaceIcon, WorkspaceMessage, XMarkIcon],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"flex flex-col gap-2\" },\n template: `\n @if (doneWorkspace(); as done) {\n <p class=\"text-sm text-(--font-primary-base)\" role=\"status\" data-testid=\"done\">\n {{ \"documentWorkspaces.picker.addedTo\" | transloco: { name: done.name } }}\n </p>\n }\n\n @if (creating()) {\n <div class=\"flex flex-col gap-1.5\" data-testid=\"new-form\">\n <label for=\"new-workspace-name\" class=\"text-xs font-semibold text-(--font-primary-base)\">{{ \"documentWorkspaces.picker.workspaceName\" | transloco }}</label>\n <div class=\"flex items-center gap-1\">\n <input-group class=\"min-w-0 flex-1\" size=\"md\" [scheme]=\"duplicate() ? 'error' : 'default'\">\n <input\n input-control\n id=\"new-workspace-name\"\n type=\"text\"\n autocomplete=\"off\"\n [formField]=\"newForm.name\"\n [attr.placeholder]=\"'documentWorkspaces.picker.namePlaceholder' | transloco\"\n [attr.aria-invalid]=\"duplicate() ? 'true' : null\"\n (keydown)=\"onNameKeydown($event)\" />\n </input-group>\n <button\n variant=\"secondary\"\n size=\"md\"\n [iconOnly]=\"true\"\n [disabled]=\"submitting()\"\n [attr.aria-label]=\"'documentWorkspaces.common.cancel' | transloco\"\n (click)=\"cancelNew()\">\n <x-mark-icon />\n </button>\n <button size=\"md\" [iconOnly]=\"true\" [disabled]=\"!canCreate() || submitting()\" [attr.aria-label]=\"'documentWorkspaces.common.create' | transloco\" (click)=\"create()\">\n <check-icon />\n </button>\n </div>\n @if (duplicate()) {\n <p role=\"alert\" class=\"text-xs text-(--font-error-base)\">{{ \"documentWorkspaces.picker.duplicateName\" | transloco }}</p>\n }\n </div>\n } @else {\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-xs font-semibold text-(--font-primary-muted)\">{{ \"documentWorkspaces.list.title\" | transloco }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"creating.set(true)\">{{ \"documentWorkspaces.picker.newWorkspace\" | transloco }}</button>\n </div>\n }\n\n <ul class=\"flex max-h-64 flex-col gap-1 overflow-auto\" [class.opacity-50]=\"creating()\" [attr.inert]=\"creating() ? '' : null\" data-testid=\"list\">\n @switch (status()) {\n @case (\"loading\") {\n <li class=\"px-1 py-2 text-sm text-(--font-primary-muted)\">{{ \"documentWorkspaces.mention.loading\" | transloco }}</li>\n }\n @case (\"error\") {\n <li class=\"flex items-center gap-2 px-1 py-2 text-sm text-(--font-error-base)\" data-testid=\"load-error\">\n {{ \"documentWorkspaces.mention.loadError\" | transloco }}\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </li>\n }\n @default {\n @for (workspace of rows(); track workspace.id) {\n <li class=\"flex items-center gap-2 rounded-lg px-1 py-1 data-new:bg-(--bg-primary-lightest)\" [attr.data-new]=\"isNew(workspace) ? '' : null\" [attr.data-id]=\"workspace.id\">\n <workspace-icon size=\"md\" [color]=\"workspace.color\" />\n <span class=\"min-w-0 flex-1 truncate text-sm text-(--font-neutral-black)\">{{ workspace.name }}</span>\n @if (doneId() === workspace.id) {\n <button size=\"sm\" [iconOnly]=\"true\" [attr.aria-label]=\"'documentWorkspaces.picker.addedTo' | transloco: { name: workspace.name }\" disabled>\n <check-icon />\n </button>\n } @else {\n <button\n variant=\"secondary\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"busyId() !== null || doneId() !== null\"\n [attr.aria-label]=\"'documentWorkspaces.picker.addTo' | transloco: { name: workspace.name }\"\n (click)=\"add(workspace)\">\n <plus-icon />\n </button>\n }\n </li>\n } @empty {\n <li class=\"px-1 py-2 text-sm text-(--font-primary-muted)\" data-testid=\"empty\">{{ \"documentWorkspaces.picker.noWorkspaces\" | transloco }}</li>\n }\n }\n }\n </ul>\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"text-xs text-(--font-error-base)\"><workspace-message [message]=\"message\" /></p>\n }\n `\n})\nexport class WorkspacePicker {\n /** `save` attaches `documents` to the chosen workspace; `move` takes `documentIds` from `fromWorkspaceId` to it. */\n readonly mode = input<\"save\" | \"move\">(\"save\");\n readonly documents = input<readonly PickerDocument[]>([]);\n readonly fromWorkspaceId = input<string | null>(null);\n readonly documentIds = input<readonly string[]>([]);\n\n /** The documents are in the workspace, and the check mark has been seen. */\n readonly completed = output<Workspace>();\n\n private readonly store = inject(WorkspacesStore);\n\n protected readonly status = signal<\"loading\" | \"ready\" | \"error\">(\"loading\");\n protected readonly busyId = signal<string | null>(null);\n protected readonly doneId = signal<string | null>(null);\n protected readonly error = signal<Message | null>(null);\n\n protected readonly creating = signal(false);\n /** The name of the workspace being made, in a form of its own. */\n private readonly newModel = signal({ name: \"\" });\n protected readonly newForm = form(this.newModel, path => maxLength(path.name, NEW_WORKSPACE_NAME_MAX));\n protected readonly newName = computed(() => this.newModel().name);\n protected readonly submitting = signal(false);\n /** The workspaces made from here, newest first: they head the list. */\n private readonly created = signal<string[]>([]);\n\n private readonly candidates = computed(() => {\n const from = this.fromWorkspaceId();\n return this.store.workspaces().filter(w => canWrite(w.currentUserRole) && w.id !== from);\n });\n protected readonly rows = computed(() => {\n const candidates = this.candidates();\n const heading = this.created()\n .map(id => candidates.find(w => w.id === id))\n .filter((w): w is Workspace => !!w);\n return [...heading, ...candidates.filter(w => !this.created().includes(w.id))];\n });\n protected readonly doneWorkspace = computed(() => this.store.workspaces().find(w => w.id === this.doneId()));\n\n /** Two workspaces with the same name cannot be told apart in a list: the name is not offered twice (whatever its case). */\n protected readonly duplicate = computed(() => {\n const name = this.newName().trim().toLowerCase();\n return !!name && this.store.workspaces().some(w => w.name.trim().toLowerCase() === name);\n });\n protected readonly canCreate = computed(() => !!this.newName().trim() && !this.duplicate());\n\n private timer: ReturnType<typeof setTimeout> | undefined;\n\n constructor() {\n // Once, when first shown: the store is shared and usually loaded already.\n void this.load();\n inject(DestroyRef).onDestroy(() => clearTimeout(this.timer));\n }\n\n protected async load(): Promise<void> {\n if (this.store.loaded()) {\n this.status.set(\"ready\");\n return;\n }\n this.status.set(\"loading\");\n try {\n await this.store.ensureLoaded();\n this.status.set(\"ready\");\n } catch {\n this.status.set(\"error\");\n }\n }\n\n protected isNew(workspace: Workspace): boolean {\n return this.created().includes(workspace.id);\n }\n\n protected async add(workspace: Workspace): Promise<void> {\n if (this.busyId() || this.doneId()) return;\n this.error.set(null);\n this.busyId.set(workspace.id);\n try {\n if (this.mode() === \"move\") {\n const from = this.fromWorkspaceId();\n const ids = this.documentIds();\n if (from && ids.length) await this.store.moveDocuments(from, workspace.id, [...ids]);\n } else if (this.documents().length) {\n await this.store.saveDocuments(workspace.id, [...this.documents()]);\n }\n } catch (error) {\n this.error.set(workspaceErrorMessage(error));\n return;\n } finally {\n this.busyId.set(null);\n }\n this.doneId.set(workspace.id);\n this.timer = setTimeout(() => this.completed.emit(workspace), this.mode() === \"move\" ? MOVE_FEEDBACK_MS : SAVE_FEEDBACK_MS);\n }\n\n protected onNameKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n void this.create();\n } else if (event.key === \"Escape\") {\n // Closes the form, not whatever is around the picker.\n event.stopPropagation();\n this.cancelNew();\n }\n }\n\n protected cancelNew(): void {\n if (this.submitting()) return;\n this.creating.set(false);\n this.newModel.set({ name: \"\" });\n this.error.set(null);\n }\n\n protected async create(): Promise<void> {\n if (this.submitting() || !this.canCreate()) return;\n this.submitting.set(true);\n this.error.set(null);\n try {\n const workspace = await this.store.create({ name: this.newName().trim() });\n this.created.update(ids => [workspace.id, ...ids]);\n this.creating.set(false);\n this.newModel.set({ name: \"\" });\n } catch (error) {\n this.error.set(workspaceErrorMessage(error));\n } finally {\n this.submitting.set(false);\n }\n }\n}\n","import { NgTemplateOutlet } from \"@angular/common\";\nimport { afterNextRender, ChangeDetectionStrategy, Component, computed, effect, inject, Injector, untracked, viewChild } from \"@angular/core\";\nimport { provideTranslocoScope, TranslocoPipe } from \"@jsverse/transloco\";\nimport {\n BreakpointObserverService,\n createCallable,\n DialogResult,\n DialogTitleComponent,\n injectCallRef,\n PopoverDirective,\n SheetBodyComponent,\n SheetComponent,\n SheetHeaderComponent\n} from \"@sinequa/galactik\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { PickerDocument, WorkspacePicker } from \"./workspace-picker\";\n\nexport interface WorkspacePickerDialogConfig {\n mode: \"save\" | \"move\";\n /** Where the popover opens from. Without one (or on a narrow window) it is a sheet. */\n anchor?: HTMLElement | null;\n /** `save`: the documents to attach, by name. */\n documents?: readonly PickerDocument[];\n /** `move`: where they come from, and which of its documents. */\n fromWorkspaceId?: string | null;\n documentIds?: readonly string[];\n}\n\n/** What the dialog resolves with once the documents are in: the workspace they went to. */\nexport type WorkspacePickerResult = { type: \"dialog-confirm\"; workspace: Workspace };\n\nlet nextId = 0;\n\n/**\n * \"Save to workspace\" / \"Move to workspace\", as a popover under the button that asked\n * (`anchor`), or a sheet at the bottom of a narrow screen.\n *\n * ```ts\n * const result = await PickWorkspace.call({ mode: \"save\", anchor: button, documents: [{ name: \"report.pdf\" }] });\n * ```\n *\n * The popover is galactik's `[popover]` — the browser's light dismiss closes it on an outside\n * press or Escape, and floating-ui keeps it under the anchor; the sheet is galactik's `Sheet`,\n * modal, with its backdrop. Which one is decided when it opens.\n */\n@Component({\n selector: \"workspace-picker-dialog\",\n imports: [NgTemplateOutlet, TranslocoPipe, DialogTitleComponent, PopoverDirective, SheetBodyComponent, SheetComponent, SheetHeaderComponent, WorkspacePicker],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ng-template #picker>\n <workspace-picker\n [mode]=\"config.mode\"\n [documents]=\"config.documents ?? []\"\n [fromWorkspaceId]=\"config.fromWorkspaceId ?? null\"\n [documentIds]=\"config.documentIds ?? []\"\n (completed)=\"complete($event)\" />\n </ng-template>\n\n @if (asPopover) {\n <div\n popover\n #popover=\"popover\"\n [id]=\"popoverId\"\n placement=\"bottom-end\"\n [offset]=\"8\"\n [matchTriggerWidth]=\"false\"\n class=\"w-[19rem] max-w-[calc(100vw-1rem)] rounded-(--radius-xl) border border-(--stroke-primary-lighter) bg-(--bg-neutral-white) p-4 text-(--font-neutral-black) shadow-[0_10px_15px_-3px_rgba(45,57,58,0.1),0_4px_6px_-2px_rgba(45,57,58,0.05)]\"\n [attr.aria-label]=\"title() | transloco\"\n (toggle)=\"onToggle($event)\">\n <h2 class=\"sr-only\">{{ title() | transloco }}</h2>\n <ng-container [ngTemplateOutlet]=\"picker\" />\n </div>\n } @else {\n <Sheet #sheet side=\"bottom\" [ariaLabel]=\"title() | transloco\" (closed)=\"onSheetClosed($event)\">\n <SheetHeader [closeLabel]=\"'documentWorkspaces.common.close' | transloco\">\n <DialogTitle>{{ title() | transloco }}</DialogTitle>\n </SheetHeader>\n <SheetBody>\n <ng-container [ngTemplateOutlet]=\"picker\" />\n </SheetBody>\n </Sheet>\n }\n `\n})\nexport class WorkspacePickerDialog {\n protected readonly call = injectCallRef<WorkspacePickerDialogConfig, DialogResult>();\n private readonly injector = inject(Injector);\n\n protected readonly config = this.call.props();\n protected readonly title = computed(() =>\n this.config.mode === \"move\" ? \"documentWorkspaces.picker.moveToWorkspace\" : \"documentWorkspaces.picker.saveToWorkspace\"\n );\n\n /** A popover when there is an anchor to hang it from and room for it; a sheet otherwise. Decided once, when it opens. */\n protected readonly asPopover = !!this.config.anchor && !inject(BreakpointObserverService).isMobile();\n protected readonly popoverId = `workspace-picker-${nextId++}`;\n\n private readonly popover = viewChild<PopoverDirective>(\"popover\");\n private readonly sheet = viewChild<SheetComponent>(\"sheet\");\n\n private outcome: DialogResult | null = null;\n private ended = false;\n\n constructor() {\n // Opened once the view is there. Registered from an effect so that it runs after the hooks of the\n // directives inside the view (the popover's wiring listens for `toggle` in its own).\n effect(() => {\n const popover = this.popover();\n const sheet = this.sheet();\n if (!popover && !sheet) return;\n untracked(() =>\n afterNextRender(\n () => {\n if (popover) this.showPopover(popover);\n else sheet?.showModal();\n },\n { injector: this.injector }\n )\n );\n });\n }\n\n /**\n * The popover finds its trigger by `popovertarget`, so the host's button gets it for as long as the\n * popover is being placed. The native behaviour of such an attribute — toggling the popover on the\n * button's own click — is not wanted: what a press on the button means is the host's business.\n */\n private showPopover(popover: PopoverDirective): void {\n const anchor = this.config.anchor!;\n anchor.setAttribute(\"popovertarget\", this.popoverId);\n popover.show();\n // After the popover has read it (on its `toggle`, a task later), not before.\n setTimeout(() => anchor.removeAttribute(\"popovertarget\"), 0);\n }\n\n protected complete(workspace: Workspace): void {\n this.outcome = { type: \"dialog-confirm\", workspace } satisfies WorkspacePickerResult;\n const popover = this.popover();\n if (popover) popover.close();\n else this.sheet()?.close(\"dialog-confirm\");\n }\n\n /** The popover is closed — by the picker, an outside press, Escape: that is the end of the call. */\n protected onToggle(event: Event): void {\n if ((event as ToggleEvent).newState === \"closed\") this.end(\"dialog-cancel\");\n }\n\n protected onSheetClosed(event: DialogResult): void {\n this.end(event);\n }\n\n private end(event: DialogResult): void {\n if (this.ended) return;\n this.ended = true;\n this.call.end(this.outcome ?? event);\n }\n}\n\n/** Imperative entry point: `await PickWorkspace.call({ mode: \"save\", anchor, documents })`. */\nexport const PickWorkspace = createCallable<WorkspacePickerDialogConfig, DialogResult>(WorkspacePickerDialog);\n","import { ChangeDetectionStrategy, Component, inject, Injector, output } from \"@angular/core\";\nimport type { PickerDocument } from \"./workspace-picker\";\nimport { PickWorkspace, WorkspacePickerResult } from \"./workspace-picker.dialog\";\n\n/**\n * Opens the \"Save to / Move to workspace\" picker from a host page's own button, and\n * says what came of it. It draws nothing itself: it is what the `lexiq-workspace-picker`\n * custom element wraps, for hosts that have no Angular component to open the dialog from.\n * In Angular, call `PickWorkspace.call(...)` directly.\n */\n@Component({\n selector: \"workspace-picker-launcher\",\n template: \"\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"hidden\" }\n})\nexport class WorkspacePickerLauncher {\n /** Documents were saved to a workspace: its name. */\n readonly saved = output<string>();\n /** Documents were moved to another workspace. */\n readonly moved = output<void>();\n\n private readonly injector = inject(Injector);\n\n /** Opens \"Save to workspace\" under `anchor`. */\n async openSaveCard(anchor: HTMLElement, options: { documents?: readonly PickerDocument[] } = {}): Promise<void> {\n const result = await PickWorkspace.call({ mode: \"save\", anchor, documents: options.documents }, { injector: this.injector });\n if (typeof result === \"object\") this.saved.emit((result as WorkspacePickerResult).workspace.name);\n }\n\n /** Opens \"Move to workspace\" under `anchor`, for documents of the workspace they come from. */\n async openMoveMenu(anchor: HTMLElement, documentIds: readonly string[], options: { fromWorkspaceId: string }): Promise<void> {\n const result = await PickWorkspace.call({ mode: \"move\", anchor, fromWorkspaceId: options.fromWorkspaceId, documentIds }, { injector: this.injector });\n if (typeof result === \"object\") this.moved.emit();\n }\n}\n","import { ChangeDetectionStrategy, Component, inject, Injector, input, model, output } from \"@angular/core\";\nimport { provideTranslocoScope } from \"@jsverse/transloco\";\nimport type { Workspace } from \"../../models/workspace.model\";\nimport { WorkspaceDetail } from \"../detail/workspace-detail\";\nimport { EditWorkspace, WorkspaceEditResult } from \"../detail/workspace-edit.dialog\";\nimport { WorkspaceList } from \"../list/workspace-list\";\nimport type { AskFollowUp } from \"../preview/document-preview-panel\";\nimport { UploadTracker } from \"../upload/upload-tracker\";\n\n/**\n * The whole workspaces view in one component: the list, a workspace with its documents\n * and preview, the dialogs they open, and the upload tracker.\n *\n * It is the piece that decides where things go — which the parts leave to their host:\n * choosing a workspace opens it, \"Add workspace\" opens the dialog and then the new\n * workspace, leaving a workspace goes back to the list. `workspaceId` is what is shown\n * (`null` is the list), two-way: a host can open a workspace, and is told which is open.\n *\n * ```html\n * <workspaces-shell uploadEndpoint=\"/tus\" [(workspaceId)]=\"id\" (askFollowUp)=\"chat.ask($event)\" />\n * ```\n *\n * The parts are exported as well (`WorkspaceList`, `WorkspaceDetail`…) for a host that\n * wants its own layout. The shell is also what the `lexiq-workspaces` custom element\n * wraps, for pages that are not Angular.\n */\n@Component({\n selector: \"workspaces-shell\",\n imports: [UploadTracker, WorkspaceDetail, WorkspaceList],\n providers: [provideTranslocoScope(\"document-workspaces\")],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: \"block h-full min-h-0 min-w-0 dark:bg-(--bg-neutral-base) dark:text-(--font-neutral-black)\", \"[class.dark]\": \"theme() === 'dark'\" },\n template: `\n @if (workspaceId(); as id) {\n <workspace-detail\n [workspaceId]=\"id\"\n [uploadEndpoint]=\"uploadEndpoint()\"\n [queryName]=\"queryName()\"\n (back)=\"workspaceId.set(null)\"\n (deleted)=\"workspaceId.set(null)\"\n (workspaceUpdated)=\"workspaceUpdated.emit($event)\"\n (askFollowUp)=\"askFollowUp.emit($event)\" />\n } @else {\n <workspace-list (opened)=\"workspaceId.set($event.id)\" (createRequested)=\"addWorkspace()\" />\n }\n <upload-tracker />\n `\n})\nexport class WorkspacesShell {\n /** The workspace shown; `null` for the list. */\n readonly workspaceId = model<string | null>(null);\n /** The tus endpoint uploads go to. */\n readonly uploadEndpoint = input(\"\");\n /** The query the preview web service renders a document for. */\n readonly queryName = input(\"_query\");\n /** `dark` turns the dark tokens on for the shell; otherwise it follows the page. */\n readonly theme = input<\"light\" | \"dark\" | null>(null);\n\n /** A workspace was created from here. */\n readonly workspaceCreated = output<Workspace>();\n /** A workspace was edited from here. */\n readonly workspaceUpdated = output<Workspace>();\n /** \"Ask follow-up\" on a previewed document. */\n readonly askFollowUp = output<AskFollowUp>();\n\n private readonly injector = inject(Injector);\n\n /** Opens the \"Add workspace\" dialog, then the workspace it made. */\n async addWorkspace(): Promise<void> {\n const result = await EditWorkspace.call({ mode: \"add\" }, { injector: this.injector });\n if (typeof result !== \"object\") return;\n const { workspace } = result as WorkspaceEditResult;\n this.workspaceCreated.emit(workspace);\n this.workspaceId.set(workspace.id);\n }\n}\n"],"mappings":"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"}