/** * Chat organisation — folder tree, colour palette, and instruction inheritance. * * Shared between the gateway (validation, prompt assembly) and the web app * (rendering, drag targets) so both sides agree on depth limits, cycle rules, * and what counts as a valid colour. */ export type ProjectKind = 'workspace' | 'folder'; /** Deeper than this and the 256px sidebar stops being readable. */ export declare const MAX_FOLDER_DEPTH = 5; /** Instructions inherited down a long chain can silently eat the context window. */ export declare const MAX_INSTRUCTION_CHARS = 8000; /** * The nine default category colours. Values are CSS colours that work on both * light and dark backgrounds; the picker may store any `#rrggbb` instead. */ export declare const PROJECT_COLORS: readonly [{ readonly token: "slate"; readonly label: "Slate"; readonly value: "#64748b"; }, { readonly token: "red"; readonly label: "Red"; readonly value: "#ef4444"; }, { readonly token: "orange"; readonly label: "Orange"; readonly value: "#f97316"; }, { readonly token: "amber"; readonly label: "Amber"; readonly value: "#f59e0b"; }, { readonly token: "green"; readonly label: "Green"; readonly value: "#22c55e"; }, { readonly token: "teal"; readonly label: "Teal"; readonly value: "#14b8a6"; }, { readonly token: "blue"; readonly label: "Blue"; readonly value: "#3b82f6"; }, { readonly token: "violet"; readonly label: "Violet"; readonly value: "#8b5cf6"; }, { readonly token: "pink"; readonly label: "Pink"; readonly value: "#ec4899"; }]; export type ProjectColorToken = (typeof PROJECT_COLORS)[number]['token']; /** * Accept either a palette token or a `#rrggbb` literal. Anything else — including * `rgb()`, `hsl()`, and bare colour names — is rejected so the value can be * dropped straight into a style attribute without an injection risk. */ export declare function normalizeProjectColor(value: unknown): string | null; /** Resolve a stored colour to something renderable; null when unset. */ export declare function resolveProjectColor(value: string | null | undefined): string | null; /** Minimum shape the tree helpers need — both gateway rows and UI records satisfy it. */ export interface ProjectTreeNodeInput { id: string; parentId?: string | null; kind?: string | null; } export interface ProjectTreeNode { project: T; depth: number; children: ProjectTreeNode[]; } /** * Build a forest from a flat list. Nodes whose parent is missing (archived, * deleted, or belonging to another user) are surfaced at the root rather than * dropped — a chat must never become unreachable because its folder vanished. */ export declare function buildProjectTree(rows: T[]): ProjectTreeNode[]; /** Depth-first flatten, parents before children — the order the sidebar renders in. */ export declare function flattenProjectTree(nodes: ProjectTreeNode[]): ProjectTreeNode[]; /** Ancestors from the root down to (but excluding) `id`. */ export declare function getProjectAncestors(rows: T[], id: string): T[]; /** Every descendant of `id`, at any depth. */ export declare function getProjectDescendantIds(rows: T[], id: string): string[]; export type MoveRejection = 'PARENT_NOT_FOUND' | 'CYCLE' | 'TOO_DEEP'; /** * Decide whether `projectId` may be re-parented under `newParentId`. * Returns null when the move is allowed, otherwise the reason it is not. * * The same function backs the API guard and the greyed-out entries in the move * menu, so the UI can never offer a target the server would reject. * * Any row may hold children, including one with a directory of its own. The * earlier "only folders may be parents" rule became a trap once a folder could * be given a directory later: attaching a repository would silently strip the * folder's ability to contain the children it already had. Which directory * applies is answered by inheritance instead — the nearest ancestor that has * one, see ProjectService.effectiveRootPath. */ export declare function validateProjectMove(rows: T[], projectId: string, newParentId: string | null): MoveRejection | null; export interface InstructionChainEntry { id: string; title: string | null; instructions: string; } /** * Render an inherited instruction chain into a single prompt block. * * Order is root → leaf, so the most specific folder speaks last and wins any * contradiction. Output is capped at `MAX_INSTRUCTION_CHARS`: entries are * dropped from the *root* end, because the leaf is the one the user just set * and the one they expect to take effect. */ export declare function renderInstructionChain(entries: InstructionChainEntry[]): string | null; //# sourceMappingURL=project-tree.d.ts.map