sessions: export as markdown — the sidebar right-click, the route, the practice join (release polish 3)
Owner round (2026-10-06): markdown export of sessions, actioned from
the sidebar context menu — "Export as Markdown…" sits with the
management actions, above Delete (the destructive action stays last).
- GET /api/chats/:id/markdown (404 unknown): the whole session as one
attachment — text/markdown; charset=utf-8, filename from the slugified
title ("relational-databases-joins-normal-forms.md", non-ascii titles
fall back to "session.md"). Both session kinds export.
- The client is a same-origin anchor download; the route's
Content-Disposition names the file.
- sessionMarkdown's user-facing debut — minted ANCHORLESS (message-id
anchors are extraction provenance, not learner-facing). The engine
joins the core barrel (pure, browser-safe) for its first consumer.
The practice join (owner-approved mid-round): practice tool results are
bare acks — statement, outcome, verdict, and the model solution all
live in the assistant's call arguments. The engine now folds them the
same way the view seam folds cards: Problem blocks render the pose's
statement (solution never leaks at pose time); Concluded blocks render
the outcome as prose ("Solved with hints.") + verdict, then the
pose-committed model solution as a blockquoted reveal via
problemInputForConclude (clean-poses-only, its refusal law). A
dismissed problem concludes as "The learner skipped this problem." —
no reveal. Errored poses render nothing (the failed-tool law).
1427 tests (route pins: headers, slug, anchorless body, 404; engine
pins: pose-time statement, the reveal, the dismissal refusal, the
errored-pose silence); tsc, lint, build green.
This commit is contained in:
@@ -864,6 +864,28 @@ function SessionsSidebar() {
|
||||
>
|
||||
Rename
|
||||
</button>
|
||||
{/* Export as Markdown (owner, release polish 2026-10-06): the
|
||||
session-markdown engine's user-facing debut — same-origin
|
||||
anchor download; the route's Content-Disposition names the
|
||||
file. Sits with the management actions, above Delete (the
|
||||
destructive action stays last — the danger-fence law). */}
|
||||
<button
|
||||
type="button"
|
||||
data-testid="ctx-export-md"
|
||||
onClick={() => {
|
||||
const chatId = ctxChatId;
|
||||
setCtxChatId(null);
|
||||
const a = document.createElement("a");
|
||||
a.href = `/api/chats/${chatId}/markdown`;
|
||||
a.download = ""; // the route's filename wins
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
}}
|
||||
className="flex items-center rounded-lg px-2.5 py-1.5 text-left text-foreground transition-colors hover:bg-accent"
|
||||
>
|
||||
Export as Markdown…
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="ctx-delete"
|
||||
|
||||
Reference in New Issue
Block a user