feat: extract review-workspace slice into @citation-evidence/work (CWORK-WP-0001)
Establishes citation-work as the standalone home of the review workspace, migrated out of the citation-evidence umbrella app. - Package/tooling scaffold: package.json, tsconfig, vite/vitest/eslint configs with eslint-plugin-boundaries enforcing engine/anchor/source-only imports - Providers/hooks: EngineProvider, SessionProvider + use* hooks (T02) - Panes: CollectionList, ViewerShell, EvidenceSidebar (T03/T04/T05) - Capture/edit: InlineCaptureForm, EvidenceFormBody; UploadDropzone owned here - ReviewShell: repo-owned three-pane layout with an upload slot seam - Tests: CollectionList, InlineCaptureForm capture flow, EvidenceSidebar export/edit/activation (11 tests, all green) - Anchor consumed as @citation-evidence/evidence-anchor package; @source kept on the umbrella facade for its local viewer-url policy - Docs (README/SCOPE) refreshed; deferred gaps recorded Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c51ef94812
commit
0cd2ca5d04
30 changed files with 7958 additions and 342 deletions
104
src/work/EvidenceFormBody.tsx
Normal file
104
src/work/EvidenceFormBody.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* EvidenceFormBody — the shared "citation + commentary" editor.
|
||||
*
|
||||
* One form drives both:
|
||||
* - the *capture* flow (creating evidence from a fresh selection
|
||||
* inside `InlineCaptureForm`), and
|
||||
* - the *edit* flow (modifying an existing evidence card inside
|
||||
* `EvidenceSidebar`).
|
||||
*
|
||||
* Keeping a single component means changes to the field layout, hint
|
||||
* placement, or save-button copy land in one file and apply to both
|
||||
* surfaces. Callers control the labels and any badge/helper text.
|
||||
*/
|
||||
|
||||
import type { CSSProperties, ReactNode } from "react";
|
||||
|
||||
export interface EvidenceFormBodyProps {
|
||||
readonly quote: string;
|
||||
readonly commentary: string;
|
||||
onChangeQuote(next: string): void;
|
||||
onChangeCommentary(next: string): void;
|
||||
onSave(): void;
|
||||
onCancel(): void;
|
||||
/** Save-button label, defaults to "Save". */
|
||||
readonly saveLabel?: string;
|
||||
/** Cancel-button label, defaults to "Cancel". */
|
||||
readonly cancelLabel?: string;
|
||||
/** Short caption rendered at the top of the form, e.g. a selector
|
||||
* count badge or "Editing". */
|
||||
readonly badge?: ReactNode;
|
||||
/** Inline note shown under the buttons (e.g. "won't move the
|
||||
* marked passage" when editing an existing item). */
|
||||
readonly helper?: ReactNode;
|
||||
/** data-testid prefix for the input + button hooks. */
|
||||
readonly testidPrefix: string;
|
||||
}
|
||||
|
||||
export function EvidenceFormBody(p: EvidenceFormBodyProps) {
|
||||
const saveLabel = p.saveLabel ?? "Save";
|
||||
const cancelLabel = p.cancelLabel ?? "Cancel";
|
||||
return (
|
||||
<div style={{ padding: 8, fontSize: 12 }}>
|
||||
{p.badge && (
|
||||
<div style={{ marginBottom: 6, fontWeight: 600 }}>{p.badge}</div>
|
||||
)}
|
||||
<label style={labelStyle}>Citation text</label>
|
||||
<textarea
|
||||
value={p.quote}
|
||||
onChange={(e) => p.onChangeQuote(e.target.value)}
|
||||
data-testid={`${p.testidPrefix}-quote`}
|
||||
rows={3}
|
||||
style={{
|
||||
...textareaStyle,
|
||||
fontStyle: "italic",
|
||||
}}
|
||||
/>
|
||||
<label style={labelStyle}>Commentary</label>
|
||||
<textarea
|
||||
value={p.commentary}
|
||||
onChange={(e) => p.onChangeCommentary(e.target.value)}
|
||||
data-testid={`${p.testidPrefix}-commentary`}
|
||||
rows={2}
|
||||
placeholder="(optional)"
|
||||
style={textareaStyle}
|
||||
/>
|
||||
<div style={{ display: "flex", gap: 6, alignItems: "center" }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={p.onSave}
|
||||
data-testid={`${p.testidPrefix}-save`}
|
||||
style={{ fontSize: 12, padding: "4px 10px" }}
|
||||
>
|
||||
{saveLabel}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={p.onCancel}
|
||||
data-testid={`${p.testidPrefix}-cancel`}
|
||||
style={{ fontSize: 12, padding: "4px 10px" }}
|
||||
>
|
||||
{cancelLabel}
|
||||
</button>
|
||||
{p.helper && (
|
||||
<span style={{ fontSize: 11, color: "#888" }}>{p.helper}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const labelStyle: CSSProperties = {
|
||||
display: "block",
|
||||
color: "#666",
|
||||
fontSize: 11,
|
||||
marginBottom: 2,
|
||||
};
|
||||
|
||||
const textareaStyle: CSSProperties = {
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
fontSize: 12,
|
||||
padding: 4,
|
||||
marginBottom: 6,
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue