@consciousclouds/assistant-surface
v0.2.0
Published
Conscious Clouds Assistant Surface — a behavior-only assistant shell: open/close lifecycle, focus management, accessibility, overlay/inflow modes, scroll lock, slots, single-surface rule. Zero appearance, zero product, zero AI.
Downloads
78
Readme
@consciousclouds/assistant-surface
A behavior-only assistant shell. It owns the behavior of a surface that hosts an assistant or a conversation — and nothing about how it looks.
It standardizes:
- open / close lifecycle
- focus management (focus moves in on open; restores to the opener on close; trapped within the surface in overlay mode)
- accessibility (dialog semantics in overlay, region in inflow, a labelling hook)
- Escape to close and outside-click to close (overlay)
- scroll lock (overlay)
- overlay mode and inflow mode
- the single-surface-per-application rule (warns if a second mounts)
It owns none of the appearance — branding, colors, typography, Tailwind, shadcn, headers, suggestions, content. Products supply those through slots.
Purity
This package imports no design system, no Canvas, no AI, and knows nothing about any product. It is so neutral that the same package serves a settings panel, a help drawer, a workflow assistant, or a future AI experience, and never knows those products exist. The test:
- Could Omni use it tomorrow without knowing ENO exists? Yes.
- Could CloudChief use it tomorrow without knowing Bacchus exists? Yes.
- Could ENO use it tomorrow without knowing the Conductor exists? Yes.
Slots
The surface exposes five slots, filled by the product and treated as opaque:
trigger, header, content, input, canvas. The canvas slot is a
behavior-managed mounting point — pass a rendered Canvas (or anything) into it;
this package never imports the Canvas Runtime.
Usage
import { AssistantSurface } from "@consciousclouds/assistant-surface";
function MySurface() {
const [open, setOpen] = React.useState(false);
return (
<AssistantSurface
mode="overlay"
open={open}
onOpenChange={setOpen}
aria-label="Assistant"
// your own components — the surface never styles them:
trigger={<MyTriggerButton />}
header={<MyHeader />}
content={<MyConversation />}
input={<MyComposer />}
// structural hooks for YOUR styling (no defaults):
className="my-panel"
scrimClassName="my-scrim"
/>
);
}The package applies only functional layout (fixed positioning for the overlay,
scroll lock); colors, size, placement, and z-index are yours via className /
style / scrimClassName.
