stuff
This commit is contained in:
+9
-7
@@ -16,22 +16,24 @@ Health
|
|||||||
|
|
||||||
Documents
|
Documents
|
||||||
---------
|
---------
|
||||||
- GET /api/documents - List or search documents. Optional filters: `folder_id` (defaults to root when omitted), `include_deleted`, `include_descendants` (defaults to true unless explicitly set to `false` without filters), `query` (Quickwit full-text), `tags` (comma-separated tag UUIDs), and `correspondents` (comma-separated correspondent UUIDs). Each entry includes tags, correspondent assignments, and current version info.
|
- GET /api/documents - List or search documents. Optional filters: `folder_id` (defaults to root when omitted), `include_descendants` (defaults to true unless explicitly set to `false` without filters), `status` (`active`, `deleted`, or `all`; defaults to `active`), `query` (Quickwit full-text), `tags` (comma-separated tag UUIDs), and `correspondents` (comma-separated correspondent UUIDs). Each entry includes tags, correspondent assignments, and current version info.
|
||||||
- GET /api/documents/check?checksum=<sha256> - Lightweight checksum preflight. Returns `exists=false` when no document with the supplied SHA-256 checksum is present; otherwise returns `exists=true` plus the current document metadata.
|
- GET /api/documents/check?checksum=<sha256> - Lightweight checksum preflight. Returns `exists=false` when no document with the supplied SHA-256 checksum is present; otherwise returns `exists=true` plus the current document metadata.
|
||||||
- POST /api/documents - Upload a document via multipart form-data. Required field: `file`. Optional fields: `title`, `folder_id`, JSON `metadata`, JSON array `tag_ids`, JSON array `correspondents` (each with `correspondent_id` and `role`), and `issued_at` (RFC3339). When `title` is supplied, the stored filename becomes `<title><original_extension>`. Include `skip_existing=true` to receive `204 No Content` instead of reusing a matching document.
|
- POST /api/documents - Upload a document via multipart form-data. Required field: `file`. Optional fields: `title`, `folder_id`, JSON `metadata`, JSON array `tag_ids`, JSON array `correspondents` (each with `correspondent_id`), and `issued_at` (RFC3339). When `title` is supplied, the stored filename becomes `<title><original_extension>`. Include `skip_existing=true` to receive `204 No Content` instead of reusing a matching document.
|
||||||
- POST /api/documents/bulk/move - Move multiple documents to a target folder.
|
- POST /api/documents/bulk/move - Move multiple documents to a target folder.
|
||||||
- POST /api/documents/bulk/tags - Add or remove tags across multiple documents.
|
- POST /api/documents/bulk/tags - Add or remove tags across multiple documents.
|
||||||
- POST /api/documents/bulk/correspondents - Bulk correspondent actions. Default `action=add` replaces existing assignments for the provided roles before adding the supplied correspondents; `action=remove` drops the specified correspondent/role pairs.
|
- POST /api/documents/bulk/correspondents - Bulk correspondent actions. Use `action=add` (default) to attach correspondents or `action=remove` to detach the provided correspondents.
|
||||||
- POST /api/documents/bulk/reanalyze - Queue re-analysis jobs for selected documents.
|
- POST /api/documents/bulk/reanalyze - Queue re-analysis jobs for selected documents.
|
||||||
- GET /api/documents/:id - Retrieve metadata and current version details for a document.
|
- GET /api/documents/:id - Retrieve metadata and current version details for a document.
|
||||||
- PATCH /api/documents/:id - Update document metadata (currently title).
|
- PATCH /api/documents/:id - Update document metadata (currently title).
|
||||||
- DELETE /api/documents/:id - Soft-delete a document.
|
- DELETE /api/documents/:id - Soft-delete a document.
|
||||||
- GET /api/documents/:id/download - Create a pre-signed download URL for the current version.
|
|
||||||
- PATCH /api/documents/:id/folder - Move a document to another folder.
|
- PATCH /api/documents/:id/folder - Move a document to another folder.
|
||||||
|
- POST /api/documents/:id/restore - Restore a soft-deleted document. Optional body `{ "folder_id": <uuid> }` to send it to a specific folder; defaults to the original folder or root if missing.
|
||||||
|
- GET /api/documents/:id/versions - List version history for a document.
|
||||||
|
- GET /api/documents/:id/versions/:version_id - Fetch metadata and assets for a specific version.
|
||||||
- POST /api/documents/:id/tags - Assign one or more tags to a document.
|
- POST /api/documents/:id/tags - Assign one or more tags to a document.
|
||||||
- DELETE /api/documents/:id/tags/:tag_id - Remove a single tag from a document.
|
- DELETE /api/documents/:id/tags/:tag_id - Remove a single tag from a document.
|
||||||
- POST /api/documents/:id/correspondents - Assign correspondents to roles (`assignments[]` with `correspondent_id` and `role`; optional `replace=true` overwrites existing assignments for those roles). Valid roles: `sender`, `receiver`, `other`.
|
- POST /api/documents/:id/correspondents - Assign correspondents (`assignments[]` with `correspondent_id`; optional `replace=true` overwrites existing assignments).
|
||||||
- DELETE /api/documents/:id/correspondents/:correspondent_id - Remove a correspondent assignment (requires `role` query string).
|
- DELETE /api/documents/:id/correspondents/:correspondent_id - Remove a correspondent assignment.
|
||||||
|
|
||||||
Document Assets
|
Document Assets
|
||||||
---------------
|
---------------
|
||||||
@@ -62,7 +64,7 @@ Tags
|
|||||||
|
|
||||||
Correspondents
|
Correspondents
|
||||||
--------------
|
--------------
|
||||||
- GET /api/correspondents - List correspondents with usage totals and per-role counts (roles: `sender`, `receiver`, `other`).
|
- GET /api/correspondents - List correspondents with usage totals.
|
||||||
- POST /api/correspondents - Create a correspondent (name + optional metadata JSON).
|
- POST /api/correspondents - Create a correspondent (name + optional metadata JSON).
|
||||||
- PATCH /api/correspondents/:id - Update name and/or metadata.
|
- PATCH /api/correspondents/:id - Update name and/or metadata.
|
||||||
- DELETE /api/correspondents/:id - Remove a correspondent; fails with 400 if referenced by any document.
|
- DELETE /api/correspondents/:id - Remove a correspondent; fails with 400 if referenced by any document.
|
||||||
|
|||||||
@@ -1291,15 +1291,30 @@ const DetailPanel = ({
|
|||||||
<aside className="detail-panel panel">
|
<aside className="detail-panel panel">
|
||||||
<div className="panel-header">
|
<div className="panel-header">
|
||||||
<div className="panel-actions">
|
<div className="panel-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="icon-button ghost"
|
className="icon-button ghost"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
aria-label="Close detail panel"
|
aria-label="Close detail panel"
|
||||||
title="Close detail panel"
|
title="Close detail panel"
|
||||||
>
|
>
|
||||||
<ChevronsRightIcon />
|
<ChevronsRightIcon />
|
||||||
</button>
|
</button>
|
||||||
|
{singleDoc ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="icon-button ghost"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
onOpenPreview(singleDoc.id);
|
||||||
|
}}
|
||||||
|
aria-label="Open preview"
|
||||||
|
title="Open preview"
|
||||||
|
disabled={!singleHasPreview}
|
||||||
|
>
|
||||||
|
<WindowMaximizeIcon />
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
<div className="spacer" />
|
<div className="spacer" />
|
||||||
{isBulkSelection && onBulkReanalyze ? (
|
{isBulkSelection && onBulkReanalyze ? (
|
||||||
<button
|
<button
|
||||||
@@ -1328,21 +1343,6 @@ const DetailPanel = ({
|
|||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
{singleDoc ? (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="icon-button ghost"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
onOpenPreview(singleDoc.id);
|
|
||||||
}}
|
|
||||||
aria-label="Open preview"
|
|
||||||
title="Open preview"
|
|
||||||
disabled={!singleHasPreview}
|
|
||||||
>
|
|
||||||
<WindowMaximizeIcon />
|
|
||||||
</button>
|
|
||||||
) : null}
|
|
||||||
{showOcrAction ? (
|
{showOcrAction ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { getAssetFromVersion, resolveDocumentAssetUrl, createAssetView } from '../asset_manager';
|
import { getAssetFromVersion, resolveDocumentAssetUrl, createAssetView } from '../asset_manager';
|
||||||
import { getTagColorStyle } from '../utils/colors';
|
import { getTagColorStyle } from '../utils/colors';
|
||||||
|
import DetailPanel from '../detail/DetailPanel';
|
||||||
import {
|
import {
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
EditIcon,
|
EditIcon,
|
||||||
@@ -1125,6 +1126,7 @@ export const createDocumentsSurface = ({
|
|||||||
parentBreadcrumb,
|
parentBreadcrumb,
|
||||||
onNavigateParent,
|
onNavigateParent,
|
||||||
renderSidebarToggle,
|
renderSidebarToggle,
|
||||||
|
detailProps,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
currentFolderName,
|
currentFolderName,
|
||||||
@@ -1174,11 +1176,22 @@ export const createDocumentsSurface = ({
|
|||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const detail = (() => {
|
||||||
|
if (!detailProps) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const count = detailProps.selectedDocuments?.length || 0;
|
||||||
|
if (!count) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return <DetailPanel {...detailProps} />;
|
||||||
|
})();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: 'documents',
|
key: 'documents',
|
||||||
variant: 'documents',
|
variant: 'documents',
|
||||||
header: { title, subtitle, leading, actions },
|
header: { title, subtitle, leading, actions },
|
||||||
content: <DocumentsTable {...tableProps} showHeader={false} />,
|
content: <DocumentsTable {...tableProps} showHeader={false} />,
|
||||||
supportsDetail: true,
|
detail,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
import './styles.css';
|
import './styles.css';
|
||||||
import AssetManager, { getAssetFromVersion, resolveDocumentAssetUrl, createAssetView } from './asset_manager';
|
import AssetManager, { getAssetFromVersion, resolveDocumentAssetUrl, createAssetView } from './asset_manager';
|
||||||
import useApiError from './hooks/useApiError';
|
import useApiError from './hooks/useApiError';
|
||||||
import DetailPanel from './detail/DetailPanel';
|
|
||||||
import TagsPanel from './tags/TagsPanel';
|
import TagsPanel from './tags/TagsPanel';
|
||||||
import CorrespondentsPanel from './correspondents/CorrespondentsPanel';
|
import CorrespondentsPanel from './correspondents/CorrespondentsPanel';
|
||||||
import TagManager from './tag_manager';
|
import TagManager from './tag_manager';
|
||||||
@@ -5485,12 +5484,14 @@ const DocumentsRoute = () => {
|
|||||||
parentBreadcrumb,
|
parentBreadcrumb,
|
||||||
onNavigateParent: parentBreadcrumb ? handleNavigateParent : null,
|
onNavigateParent: parentBreadcrumb ? handleNavigateParent : null,
|
||||||
renderSidebarToggle,
|
renderSidebarToggle,
|
||||||
|
detailProps: detailPanelProps,
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
documentsTableProps,
|
documentsTableProps,
|
||||||
parentBreadcrumb,
|
parentBreadcrumb,
|
||||||
handleNavigateParent,
|
handleNavigateParent,
|
||||||
renderSidebarToggle,
|
renderSidebarToggle,
|
||||||
|
detailPanelProps,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const previewSurface = useMemo(() => {
|
const previewSurface = useMemo(() => {
|
||||||
@@ -5550,16 +5551,13 @@ const DocumentsRoute = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportsDetail = Boolean(surface.supportsDetail);
|
|
||||||
const detailCount = detailPanelProps.selectedDocuments?.length || 0;
|
|
||||||
const showDetailPanel = supportsDetail && detailCount > 0;
|
|
||||||
const variant = surface.variant || 'documents';
|
const variant = surface.variant || 'documents';
|
||||||
|
|
||||||
const mainContentClass = `main-content main-content--${variant}${
|
const mainContentClass = `main-content main-content--${variant}${
|
||||||
showDetailPanel ? ' main-content--has-detail' : ''
|
surface.detail ? ' main-content--has-detail' : ''
|
||||||
}`;
|
}`;
|
||||||
const bodyClass = `main-content__body main-content__body--${variant}${
|
const bodyClass = `main-content__body main-content__body--${variant}${
|
||||||
showDetailPanel ? ' main-content__body--has-detail' : ''
|
surface.detail ? ' main-content__body--has-detail' : ''
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const header = surface.header || null;
|
const header = surface.header || null;
|
||||||
@@ -5586,7 +5584,7 @@ const DocumentsRoute = () => {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className={bodyClass}>{surface.content}</div>
|
<div className={bodyClass}>{surface.content}</div>
|
||||||
{showDetailPanel ? <DetailPanel {...detailPanelProps} /> : null}
|
{surface.detail || null}
|
||||||
</div>
|
</div>
|
||||||
</DocumentsLayout>
|
</DocumentsLayout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { resolveDocumentAssetUrl } from '../asset_manager';
|
import { resolveDocumentAssetUrl } from '../asset_manager';
|
||||||
import { formatFileSize } from '../utils/format';
|
import { formatFileSize } from '../utils/format';
|
||||||
import { DownloadIcon, TextScanIcon, AnalyzeIcon, CloseIcon } from '../ui/icons';
|
import { DownloadIcon, TextScanIcon, AnalyzeIcon, CloseIcon } from '../ui/icons';
|
||||||
@@ -17,10 +17,59 @@ const PreviewWorkspace = ({
|
|||||||
const sizeLabel = sizeBytes > 0 ? formatFileSize(sizeBytes) : null;
|
const sizeLabel = sizeBytes > 0 ? formatFileSize(sizeBytes) : null;
|
||||||
const metadata =
|
const metadata =
|
||||||
document.metadata && Object.keys(document.metadata).length > 0 ? document.metadata : null;
|
document.metadata && Object.keys(document.metadata).length > 0 ? document.metadata : null;
|
||||||
|
const folderName = document.folder_path || document.folder_name || null;
|
||||||
|
const issuedAt = document.issued_at || document.current_version?.issued_at || null;
|
||||||
|
const createdAt = document.created_at || null;
|
||||||
|
const updatedAt = document.updated_at || null;
|
||||||
|
const tags = Array.isArray(document.tags) ? document.tags : [];
|
||||||
|
const correspondents = Array.isArray(document.correspondents) ? document.correspondents : [];
|
||||||
|
|
||||||
|
const metadataSummary = useMemo(() => {
|
||||||
|
const rows = [];
|
||||||
|
if (mime) rows.push(['Type', mime]);
|
||||||
|
if (sizeLabel) rows.push(['Size', sizeLabel]);
|
||||||
|
if (issuedAt) rows.push(['Issued', new Date(issuedAt).toLocaleString()]);
|
||||||
|
if (createdAt) rows.push(['Created', new Date(createdAt).toLocaleString()]);
|
||||||
|
if (updatedAt) rows.push(['Updated', new Date(updatedAt).toLocaleString()]);
|
||||||
|
if (folderName) rows.push(['Folder', folderName]);
|
||||||
|
if (tags.length) {
|
||||||
|
rows.push(['Tags', tags.map((tag) => tag.label || tag.name || tag.slug).filter(Boolean).join(', ')]);
|
||||||
|
}
|
||||||
|
if (correspondents.length) {
|
||||||
|
rows.push([
|
||||||
|
'Correspondents',
|
||||||
|
correspondents
|
||||||
|
.map((entry) => entry.name || entry.label || entry.slug)
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(', '),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}, [mime, sizeLabel, issuedAt, createdAt, updatedAt, folderName, tags, correspondents]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="preview-workspace">
|
<section className="preview-workspace">
|
||||||
<div className="preview-workspace__body">
|
<aside className="preview-workspace__sidebar">
|
||||||
|
<div className="preview-workspace__info">
|
||||||
|
{metadataSummary.length ? (
|
||||||
|
<dl className="preview-workspace__summary">
|
||||||
|
{metadataSummary.map(([label, value]) => (
|
||||||
|
<div className="preview-workspace__summary-row" key={label}>
|
||||||
|
<dt>{label}</dt>
|
||||||
|
<dd>{value || '—'}</dd>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{metadata ? (
|
||||||
|
<section className="preview-workspace__metadata">
|
||||||
|
<h4>Metadata payload</h4>
|
||||||
|
<pre>{JSON.stringify(metadata, null, 2)}</pre>
|
||||||
|
</section>
|
||||||
|
) : null}
|
||||||
|
</aside>
|
||||||
|
<div className="preview-workspace__viewer">
|
||||||
{!previewEntry?.url ? (
|
{!previewEntry?.url ? (
|
||||||
<div className="preview-workspace__message">Loading preview…</div>
|
<div className="preview-workspace__message">Loading preview…</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -31,14 +80,6 @@ const PreviewWorkspace = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{metadata ? (
|
|
||||||
<section className="preview-workspace__metadata">
|
|
||||||
<h3>Metadata</h3>
|
|
||||||
{sizeLabel ? <p><strong>Size:</strong> {sizeLabel}</p> : null}
|
|
||||||
<p><strong>Type:</strong> {mime}</p>
|
|
||||||
<pre>{JSON.stringify(metadata, null, 2)}</pre>
|
|
||||||
</section>
|
|
||||||
) : null}
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -52,7 +93,6 @@ export const createPreviewWorkspaceHeaderActions = ({
|
|||||||
resolveApiPath,
|
resolveApiPath,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
onRegenerate,
|
onRegenerate,
|
||||||
onClose,
|
|
||||||
}) => {
|
}) => {
|
||||||
if (!document) {
|
if (!document) {
|
||||||
return null;
|
return null;
|
||||||
@@ -101,11 +141,11 @@ export const createPreviewWorkspaceHeaderActions = ({
|
|||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
{hasOcr ? (
|
{hasOcr ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="icon-button ghost"
|
className="icon-button ghost"
|
||||||
onClick={handleOcrClick}
|
onClick={handleOcrClick}
|
||||||
aria-label="View OCR text"
|
aria-label="View OCR text"
|
||||||
title="View OCR text"
|
title="View OCR text"
|
||||||
>
|
>
|
||||||
<TextScanIcon />
|
<TextScanIcon />
|
||||||
@@ -120,15 +160,6 @@ export const createPreviewWorkspaceHeaderActions = ({
|
|||||||
>
|
>
|
||||||
<AnalyzeIcon />
|
<AnalyzeIcon />
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="icon-button ghost"
|
|
||||||
onClick={() => onClose?.()}
|
|
||||||
aria-label="Close preview"
|
|
||||||
title="Close preview"
|
|
||||||
>
|
|
||||||
<CloseIcon />
|
|
||||||
</button>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -150,7 +181,27 @@ export const createPreviewSurface = ({
|
|||||||
|
|
||||||
const title = document.title || document.original_name || 'Document preview';
|
const title = document.title || document.original_name || 'Document preview';
|
||||||
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
|
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
|
||||||
const leading = sidebarToggle ? <>{sidebarToggle}</> : null;
|
const closeButton = onClose
|
||||||
|
? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="icon-button ghost"
|
||||||
|
onClick={() => onClose?.()}
|
||||||
|
aria-label="Close preview"
|
||||||
|
title="Close preview"
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
const leading = sidebarToggle || closeButton
|
||||||
|
? (
|
||||||
|
<>
|
||||||
|
{sidebarToggle}
|
||||||
|
{closeButton}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
: null;
|
||||||
const header = {
|
const header = {
|
||||||
title,
|
title,
|
||||||
subtitle: null,
|
subtitle: null,
|
||||||
@@ -162,7 +213,6 @@ export const createPreviewSurface = ({
|
|||||||
resolveApiPath,
|
resolveApiPath,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
onRegenerate,
|
onRegenerate,
|
||||||
onClose,
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+87
-21
@@ -11,7 +11,7 @@
|
|||||||
--fg: oklch(0.32 0.005 calc(270deg + var(--warmth)));
|
--fg: oklch(0.32 0.005 calc(270deg + var(--warmth)));
|
||||||
--muted: oklch(0.54 0.008 calc(270deg + var(--warmth)));
|
--muted: oklch(0.54 0.008 calc(270deg + var(--warmth)));
|
||||||
--sidebar-fg: oklch(0.56 0.007 calc(270deg + var(--warmth)));
|
--sidebar-fg: oklch(0.56 0.007 calc(270deg + var(--warmth)));
|
||||||
--border: oklch(0.88 0.002 calc(270deg + var(--warmth)));
|
--border: oklch(0.92 0.002 calc(270deg + var(--warmth)));
|
||||||
|
|
||||||
/* --- Accent (primary) --- */
|
/* --- Accent (primary) --- */
|
||||||
--accent: oklch(0.61 0.20 calc(260deg + var(--warmth)));
|
--accent: oklch(0.61 0.20 calc(260deg + var(--warmth)));
|
||||||
@@ -445,6 +445,10 @@ button.danger:hover:not([disabled]) {
|
|||||||
box-shadow: -12px 0 24px -12px var(--shadow-faint);
|
box-shadow: -12px 0 24px -12px var(--shadow-faint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.documents-main:not(.documents-main--sidebar-collapsed) .main-content {
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
.main-content__header {
|
.main-content__header {
|
||||||
padding: 0.5rem 1.25rem;
|
padding: 0.5rem 1.25rem;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -538,9 +542,9 @@ button.danger:hover:not([disabled]) {
|
|||||||
.preview-workspace {
|
.preview-workspace {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
gap: 1.5rem;
|
||||||
gap: 0.75rem;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace__header {
|
.preview-workspace__header {
|
||||||
@@ -672,17 +676,84 @@ button.danger:hover:not([disabled]) {
|
|||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace__body {
|
.preview-workspace__sidebar {
|
||||||
flex: 1;
|
flex: 0 0 20em;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
justify-content: center;
|
gap: 1rem;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__info {
|
||||||
|
background: var(--surface-subtle);
|
||||||
|
padding: 1rem;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__summary {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__summary-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__summary-row dt {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__summary-row dd {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__metadata {
|
||||||
|
background: var(--surface-subtle);
|
||||||
|
padding: 1rem;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 40vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__metadata h4 {
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__metadata pre {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-workspace__viewer {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
background: var(--surface-subtle);
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace__object {
|
.preview-workspace__object {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tags-panel__body {
|
.tags-panel__body {
|
||||||
@@ -865,20 +936,14 @@ button.danger:hover:not([disabled]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace__message {
|
.preview-workspace__message {
|
||||||
color: var(--muted);
|
position: absolute;
|
||||||
font-size: 0.95rem;
|
top: 50%;
|
||||||
}
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
.preview-workspace__metadata {
|
color: #fff;
|
||||||
border-radius: 2px;
|
background: rgba(0, 0, 0, 0.65);
|
||||||
padding: 0.75rem;
|
padding: 0.5rem 1rem;
|
||||||
background: var(--surface);
|
border-radius: 999px;
|
||||||
max-height: 180px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-workspace__metadata h3 {
|
|
||||||
margin: 0 0 0.5rem;
|
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1948,6 +2013,7 @@ button.danger:hover:not([disabled]) {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
box-shadow: 0 0 24px var(--shadow-soft);
|
box-shadow: 0 0 24px var(--shadow-soft);
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
|
|||||||
Reference in New Issue
Block a user