feat: expose defense qna in presenter sidebar
This commit is contained in:
@@ -43,5 +43,7 @@ describe("PresenterRoute", () => {
|
||||
expect(screen.getByRole("heading", { name: /Where is the AI agent/i })).toBeInTheDocument();
|
||||
expect(screen.getByText(/Answer directly first/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Abstract; Chapter 1 framing/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Defense Q&A/i).closest("details")).toHaveAttribute("open");
|
||||
expect(screen.getByRole("link", { name: /Where is the AI agent in this thesis/i })).toHaveAttribute("aria-current", "page");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,7 +35,11 @@ export const PresenterRoute = () => {
|
||||
const discussion = navigation.location.kind === "discussion" ? findDiscussionBranch(navigation.location.branchId) : undefined;
|
||||
|
||||
return (
|
||||
<PresenterShell current={navigation.note} covered={covered}>
|
||||
<PresenterShell
|
||||
current={navigation.note}
|
||||
covered={covered}
|
||||
activeDiscussionId={navigation.location.kind === "discussion" ? navigation.location.branchId : null}
|
||||
>
|
||||
{navigation.note && (
|
||||
<PresenterNavigationBar
|
||||
currentIndex={navigation.index}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import type { ReactNode } from "react";
|
||||
import type { PresenterBeatNote } from "./presenter-notes.js";
|
||||
import type { DiscussionBranchId } from "../storyboard.js";
|
||||
import { PresenterSidebar } from "./PresenterSidebar.js";
|
||||
|
||||
type PresenterShellProps = {
|
||||
readonly current: PresenterBeatNote | null;
|
||||
readonly covered: ReadonlySet<string>;
|
||||
readonly activeDiscussionId: DiscussionBranchId | null;
|
||||
readonly children: ReactNode;
|
||||
};
|
||||
|
||||
export const PresenterShell = ({ current, covered, children }: PresenterShellProps) => (
|
||||
export const PresenterShell = ({ current, covered, activeDiscussionId, children }: PresenterShellProps) => (
|
||||
<main className="presenter-route" aria-label="lda.chat presenter notes">
|
||||
<PresenterSidebar current={current} covered={covered} />
|
||||
<PresenterSidebar current={current} covered={covered} activeDiscussionId={activeDiscussionId} />
|
||||
<div className="presenter-route__reader">{children}</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
import { mainScenes } from "../storyboard.js";
|
||||
import { discussionBranches, mainScenes, type DiscussionBranchId } from "../storyboard.js";
|
||||
import { presenterSceneNotes, type PresenterBeatNote } from "./presenter-notes.js";
|
||||
import { presenterHashForNote } from "./presenter-navigation.js";
|
||||
|
||||
type PresenterSidebarProps = {
|
||||
readonly current: PresenterBeatNote | null;
|
||||
readonly covered: ReadonlySet<string>;
|
||||
readonly activeDiscussionId: DiscussionBranchId | null;
|
||||
};
|
||||
|
||||
export const PresenterSidebar = ({ current, covered }: PresenterSidebarProps) => (
|
||||
export const PresenterSidebar = ({ current, covered, activeDiscussionId }: PresenterSidebarProps) => (
|
||||
<nav className="presenter-sidebar" aria-label="Presenter scene index">
|
||||
<a className="presenter-sidebar__brand" href="#scene/thesis/title">lda.chat defense</a>
|
||||
<details className="presenter-sidebar__qna" open={activeDiscussionId !== null}>
|
||||
<summary>Defense Q&A <span>{discussionBranches.length}</span></summary>
|
||||
<ul>
|
||||
{discussionBranches.map((branch) => (
|
||||
<li key={branch.id}>
|
||||
<a
|
||||
href={`#discuss/${branch.id}`}
|
||||
aria-current={activeDiscussionId === branch.id ? "page" : undefined}
|
||||
>
|
||||
{"question" in branch && branch.question ? branch.question : branch.title}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</details>
|
||||
<ol>
|
||||
{mainScenes.map((scene, sceneIndex) => {
|
||||
const notes = presenterSceneNotes(scene.id);
|
||||
|
||||
@@ -27,6 +27,14 @@
|
||||
.presenter-sidebar__beats a { width: 1.55rem; height: 1.55rem; display: grid; place-items: center; border: 1px solid #c8c5bf; color: inherit; font: 0.7rem "IBM Plex Mono", monospace; text-decoration: none; }
|
||||
.presenter-sidebar__beats a[aria-current="page"] { border-color: #1e6b55; background: #1e6b55; color: white; }
|
||||
.presenter-sidebar__beats a[data-covered="true"]:not([aria-current="page"]) { background: #dce9e2; }
|
||||
.presenter-sidebar__qna { margin: 0 0 1.15rem; padding: 0.8rem 0; border-block: 1px solid #aaa79f; }
|
||||
.presenter-sidebar__qna summary { cursor: pointer; font: 700 1rem "Barlow Condensed", sans-serif; text-transform: uppercase; }
|
||||
.presenter-sidebar__qna summary span { margin-left: 0.3rem; color: #67655f; font: 0.68rem "IBM Plex Mono", monospace; }
|
||||
.presenter-sidebar__qna ul { display: grid; gap: 0.4rem; margin: 0.7rem 0 0; padding: 0; list-style: none; }
|
||||
.presenter-sidebar__qna li { display: block; opacity: 1; }
|
||||
.presenter-sidebar__qna a { display: block; color: #56544f; font-size: 0.82rem; line-height: 1.2; text-decoration: none; }
|
||||
.presenter-sidebar__qna a:hover, .presenter-sidebar__qna a:focus-visible { color: #155b49; text-decoration: underline; text-underline-offset: 0.15em; }
|
||||
.presenter-sidebar__qna a[aria-current="page"] { color: #155b49; font-weight: 700; }
|
||||
|
||||
.presenter-route__reader { min-width: 0; padding: 1.25rem clamp(1.5rem, 5vw, 6rem) 5rem; }
|
||||
.presenter-navigation { max-width: 72ch; min-height: 2.5rem; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 1rem; margin: 0 auto 2rem; border-bottom: 1px solid #d7d5d0; color: #67655f; font: 0.78rem "IBM Plex Mono", monospace; }
|
||||
|
||||
Reference in New Issue
Block a user