import type { CanvasNode } from "../graph-projection.js"; import type { ConceptFacet, ProjectionDefinition, ProjectionExclusion, ProjectionQuery } from "../projection.js"; import type { VisualViewOperation, VisualViewSummary } from "../adapters/visual/protocol-contract.js"; import type { VisualNextQuestion } from "../adapters/visual/wire.js"; import { type QuestionVerb } from "./question-verbs.js"; import type { ActiveFilter } from "./state.js"; import { type PresentationFlag } from "./query-fields.js"; /** * The collapsible tabbed panel along the foot of the canvas column, and its * first tab: the view's query (#248). * * It spans the CANVAS COLUMN rather than the window - the right column runs * full height and its own foot is chat - so it is mounted inside the diagram * workspace and not beside it. * * The tab is the query editor the facets never quite were. A filter panel could * compose a query and narrow the diagram, but it opened OVER the canvas it was * narrowing and it could only report what matched. Three things move the * editing here: a live match count, so a query that selects nothing is visible * before it is saved; the subjects the query drops with the facet that dropped * each one; and the projection document the query resolves to, serialised by * the same `yaml` the runtime writes it with, so what the reviewer reads is * what a commit would put on disk. * * An edit STAGES rather than saves. A query edit is a change to a view like any * other change, so it joins the changeset and lands with everything else. */ /** How long the reviewer must pause before an edit becomes a live query. */ export declare const APPLY_DEBOUNCE_MS = 300; /** How many subjects one exclusion group shows before it counts the rest. */ export declare const EXCLUSION_PREVIEW = 12; export declare const BOTTOM_PANEL_TABS: readonly [{ readonly id: "next-question"; readonly label: "Next question"; }, { readonly id: "view-query"; readonly label: "View query"; }]; export type BottomPanelTabId = (typeof BOTTOM_PANEL_TABS)[number]["id"]; export declare const sameQuery: (left: ProjectionQuery, right: ProjectionQuery) => boolean; /** * Why a subject the canvas does not draw is not drawn. * * A facet is the answer `explainProjection` gives. `isolatedConcepts` is the * only other way a concept leaves the drawn set - `relationships`/ * `isolatedConcepts` run AFTER the facets, and dropping an unconnected concept * is the one thing they do that no facet reports. `unreported` is a chat * filter, whose answer carries no reasons at all: saying nothing is the honest * reading, and claiming a facet would be an invention. */ export type ExclusionReason = ConceptFacet | "isolatedConcepts" | "unreported"; export declare const EXCLUSION_LABELS: { readonly [Reason in ExclusionReason]: string; }; export interface ExclusionGroup { readonly reason: ExclusionReason; readonly label: string; readonly subjects: readonly { readonly id: string; readonly title: string; }[]; } /** * The subjects the query drops, grouped by the facet that dropped each one. * * The SET comes from `matchedIds` and the REASON from `excluded`, deliberately: * `matchedIds` is what the canvas draws, so a list built from it can never * disagree with the diagram beside it. The exclusions answer a different * question - which facet rejected a concept - and a concept a facet rejected * can still be drawn, because `relationships: connected` pulls in the other end * of a relationship whatever the facets said about it. */ export declare const exclusionGroups: (nodes: readonly CanvasNode[], matchedIds: readonly string[] | null, excluded: readonly ProjectionExclusion[] | null) => readonly ExclusionGroup[]; /** * How many SUBJECTS the query matches, which is not the size of the match set: * `matchedIds` names concepts and relationships together, so counting it whole * reports five for a view over three components with two relationships between * them, and the reviewer counting boxes finds three. */ export declare const matchedSubjectCount: (nodes: readonly CanvasNode[], matchedIds: readonly string[] | null) => number; /** * Subjects a facet rejected that the canvas draws anyway. Not a contradiction: * `relationships: connected` selects a relationship on one endpoint and takes * the other endpoint with it. Reported because a query whose facets say one * thing and whose canvas shows another is worth one line of explanation. */ export declare const pulledBackIn: (matchedIds: readonly string[] | null, excluded: readonly ProjectionExclusion[] | null) => number; /** The presentation a reviewer can change from this tab: the three badge * toggles and the kind-label toggle (ADR 0147). */ export interface BadgeChoices { readonly showLifecycle: boolean; readonly showEvidence: boolean; readonly showOwnership: boolean; readonly showResponsibility: boolean; readonly showKindLabels: boolean; } export interface ViewDocumentInput { readonly view: VisualViewSummary; readonly query: ProjectionQuery; /** What the canvas is showing now. */ readonly badges: BadgeChoices; /** * What it was showing when this view was opened. * * Without it, a flag the view never declared gets written anyway: not one * projection in this repository declares `showOwnership`, so staging a query * edit would quietly add three presentation fields the author never wrote and * the reviewer never chose. A flag is written when the view already declares * it, or when the reviewer has actually moved it. */ readonly opened: BadgeChoices; } /** * The projection document this query resolves to, for the view being edited. */ export declare const viewDocument: ({ view, query, badges, opened, }: ViewDocumentInput) => ProjectionDefinition; /** The staged write this tab's Stage button issues. Nothing reaches disk until * the changeset is committed (ADR 0103). */ export declare const stagedViewEdit: (input: ViewDocumentInput) => VisualViewOperation; /** * Whether staging would change the view's document at all. * * Compared field by field rather than as serialised text: a query read back off * a YAML document holds its fields in whatever order the author wrote them, and * the form composes its own order, so comparing the two documents as strings * reports every view as edited the moment it is opened. */ export declare const documentChanged: (input: ViewDocumentInput) => boolean; export interface QueryPanelProps { /** Every concept the workspace declares, not the ones on screen: the * excluded list is the difference between the two. */ readonly nodes: readonly CanvasNode[]; readonly activeFilter: ActiveFilter | null; /** The view a staged edit would write, or null when no view is active. */ readonly view: VisualViewSummary | null; readonly open: boolean; readonly tab: BottomPanelTabId; readonly showLifecycle: boolean; readonly showEvidence: boolean; readonly showOwnership: boolean; readonly showResponsibility: boolean; readonly showKindLabels: boolean; readonly showNudges: boolean; readonly onTogglePresentation: (flag: PresentationFlag, value: boolean) => void; readonly onToggleOpen: () => void; readonly onSelectTab: (tab: BottomPanelTabId) => void; /** Applies the edited query to the canvas. Live, debounced, and never a * write: what is on screen is what the reviewer is composing. */ readonly onApply: (query: ProjectionQuery) => void; readonly onStage: (operation: VisualViewOperation) => void; /** * A viewer, not an author (#298): the fields still narrow the canvas - a * live filter writes nothing - but the affordance that stages the edit as a * view change is absent. The document still shows, because it is a fact. */ readonly readOnly?: boolean; /** * The next load-bearing open question (#534, ADR 0154), from the overlay. * Absent, the Next question tab is not offered and the panel is the query * panel it was. Present, the collapsed strip says the question and the tab * carries it with the same verb and assistant door the questions pane * draws, plus a way to go to its subject. */ readonly next?: VisualNextQuestion; readonly delegateLabel?: string; readonly onNextVerb?: (verb: QuestionVerb, subjectId: string | null) => void; readonly onNextDelegate?: (next: VisualNextQuestion) => void; readonly onNextGo?: (next: VisualNextQuestion) => void; } export declare function QueryPanel({ nodes, activeFilter, view, open, tab, showLifecycle, showEvidence, showOwnership, showResponsibility, showKindLabels, showNudges, onTogglePresentation, onToggleOpen, onSelectTab, onApply, onStage, readOnly, next, delegateLabel, onNextVerb, onNextDelegate, onNextGo, }: QueryPanelProps): import("react").JSX.Element;