Sign in
Join now
scn-chat
sharedcomputer.network
/
Star
Starred
Star
Something went wrong. Try again.
Fork
0
Eye filled
Watch
Watching
Watch
ATProto-based AI Assistant Chat interface.
Star
Starred
Star
Something went wrong. Try again.
Fork
Eye filled
Watch
Watching
Watch
Overview
Overview
Issues
Issues
1
1
Pulls
Pulls
Pipelines
Pipelines
scn-chat
/
apps
/
web
/
src
/
features
/
conversation
/
components
24 files
at
admin-config-export
pouriade.com
·
The composer decides whether a message can be sent once, as canSend, which both disables Send and stops a submit, so a new reason not to send can't reach one and miss the other. Submitting still checks on its own for a send already on its way and for a running reply, whose Stop takes Send's place.
·
2w ago
ceed41b6
AttachmentChips.tsx
A file in a sent message shows as the chip it was in the composer, with its icon, name, and kind and size, as "PDF · 1.2 MB", rather than a bare link holding its name. The chip moves to src/shared/ui as FileChip, which the composer and the message share. The whole chip is a link: a PDF opens in a new tab in the browser's own viewer, ending in an arrow out, and any other file downloads under its name, ending in a download arrow. Files side by side share a row that wraps toward the message's edge. The blob routes, the owner's and the shared one, serve a PDF inline and take application/pdf as a type, so a spaces user's PDF reaches the browser as one. The attachments, web UI, and styling specs say so.
2w ago
BranchComposer.tsx
The web app's message records are typed as the lexicon's MessageRecord, and a conversation's info as InfoRecord, from the local copy, which holds only records the server validated, through BranchMessage to the message, status, details, part, and tool views, so their reads need no casts. src/shared/message.ts holds what a status means, isPending and isFinished, which counts a status another client wrote that this app doesn't know as neither, along with a message's parts, whether it is encrypted, and who wrote it, in place of the checks written out in the message view, the message actions, the chat, the reply follower, and tool status. Tests build records with messageRecord in tests/helpers/records.ts, and the status rules are tested once.
2w ago
BranchView.tsx
A pending reply is drawn by one module, PendingReply.tsx: its waiting or live header, its stream, and the lines about a wait, a busy provider, or a lost stream, until its finished record replaces it. MessageView uses it while a record is pending, and it follows the conversation the FollowedConversation context names, which ConversationView provides, so MessageView and BranchView no longer take pending and live slots and BranchRow no longer checks the status. The stream's part views get the pending record rather than a made-up one, and the model's name comes from MessageView alone. LiveAuthor.tsx, ReplyStream.tsx, and StreamingReply.tsx are gone. A conversation test streams a reply, then has its record arrive, and checks the time it thought carries over and its text shows once.
2w ago
BusyBanner.tsx
The app has a description, set in the admin area beside its name, which the login page shows under the name in place of its fixed tagline. Every page carries it as its meta description and in Open Graph tags for link previews, with robots noindex, since each page is someone's own. A page's title is its route's staticData.title, such as Settings, Admin, or Shared chat, before the app name. The login page is the one page search engines may list: it overrides robots and names its canonical URL and og:url under PUBLIC_URL, which the server now passes with each request. /robots.txt keeps crawlers out of /api, /oauth, and /xrpc. shared/app-name.ts becomes shared/site.ts, which reads the name and description from the rendered page in the browser.
2w ago
composer-parts.tsx
On a reload, the composer's box takes typing before the page's code loads. The page's frame, which the server sends in place of the client-only chats, now holds the composer's frame at the foot of the panel: a plain textarea in the composer's card, with Attach, the model select's loading line, and Send disabled, so Enter only adds a line. The conversation's column shows it again while its code downloads. Each frame that gives way leaves its text, cursor, and focus in composer-handoff.ts, and the frame or composer that takes its place puts them back, so the swap can't be seen. Only a composer that starts empty takes a draft, never one editing a message. The frame and the composer are built from the same parts in composer-parts.tsx, and the composer binds its form field to MessageBox. An open chat's box says neither "Ask anything" nor "Reply" until its messages arrive, and sends nothing until then, since the message wouldn't know which one it answers. On a reload the turn skeleton in the page's frame shows at once, and the skeletons that take its place carry that on rather than vanishing to fade in again. The web UI spec describes it, with tests, including one that hydrates the server's frame, and an acceptance criterion.
2w ago
Composer.tsx
The composer decides whether a message can be sent once, as canSend, which both disables Send and stops a submit, so a new reason not to send can't reach one and miss the other. Submitting still checks on its own for a send already on its way and for a running reply, whose Stop takes Send's place.
2w ago
ComposerFrame.tsx
On a reload, the composer's box takes typing before the page's code loads. The page's frame, which the server sends in place of the client-only chats, now holds the composer's frame at the foot of the panel: a plain textarea in the composer's card, with Attach, the model select's loading line, and Send disabled, so Enter only adds a line. The conversation's column shows it again while its code downloads. Each frame that gives way leaves its text, cursor, and focus in composer-handoff.ts, and the frame or composer that takes its place puts them back, so the swap can't be seen. Only a composer that starts empty takes a draft, never one editing a message. The frame and the composer are built from the same parts in composer-parts.tsx, and the composer binds its form field to MessageBox. An open chat's box says neither "Ask anything" nor "Reply" until its messages arrive, and sends nothing until then, since the message wouldn't know which one it answers. On a reload the turn skeleton in the page's frame shows at once, and the skeletons that take its place carry that on rather than vanishing to fade in again. The web UI spec describes it, with tests, including one that hydrates the server's frame, and an acceptance criterion.
2w ago
ConversationFrame.tsx
A conversation on its way shows a few paragraphs of text lines, spaced as a reply's are, in place of one turn's bubble, name, and paragraph, which read as a chat with one message rather than messages loading. MessagesSkeleton replaces TurnSkeleton, and the shared page shows it under its title too. The page's frame, which the server sends, now holds the conversation header's row with a line for its title, so the lines start where they will once the conversation loads, rather than under the sidebar's toggle on a narrow screen. The header's row and its title on its way live in HeaderFrame.tsx, which the header and the frame share, so the frame stays free of the header's panels and menus. The web UI spec describes it.
2w ago
ConversationHeader.tsx
The chat list and its rows do less work, from an audit against React performance practice. Opening a search hides the chat list inside React's Activity rather than unmounting it, so closing the search brings the chats back as they were rather than mounting every row again. A chat row no longer holds three mutations: renaming and tagging are an EditChat card and deleting a DeleteChat card, each holding its own, so an idle row holds none, and cancelling a failed change clears its error with the card. Picking a tag keeps the rows shown until its chats arrive, rather than a skeleton between. The month headings of older chats come from two formatters made once, rather than one made for each chat on every change to the list. A reply's sources and a search's results take the fold's ref apart, so React Compiler compiles them rather than silently leaving them out. The conversation's rename, instructions, and sync call their mutations' own functions, so the header no longer renders with every state either passes through, and its jump outline is built only while the jump stack is open. The search spec says the list stays mounted, and the list test checks it is hidden.
2w ago
ConversationSkeleton.tsx
A conversation on its way shows a few paragraphs of text lines, spaced as a reply's are, in place of one turn's bubble, name, and paragraph, which read as a chat with one message rather than messages loading. MessagesSkeleton replaces TurnSkeleton, and the shared page shows it under its title too. The page's frame, which the server sends, now holds the conversation header's row with a line for its title, so the lines start where they will once the conversation loads, rather than under the sidebar's toggle on a narrow screen. The header's row and its title on its way live in HeaderFrame.tsx, which the header and the frame share, so the frame stays free of the header's panels and menus. The web UI spec describes it.
2w ago
ConversationView.tsx
The web app's message records are typed as the lexicon's MessageRecord, and a conversation's info as InfoRecord, from the local copy, which holds only records the server validated, through BranchMessage to the message, status, details, part, and tool views, so their reads need no casts. src/shared/message.ts holds what a status means, isPending and isFinished, which counts a status another client wrote that this app doesn't know as neither, along with a message's parts, whether it is encrypted, and who wrote it, in place of the checks written out in the message view, the message actions, the chat, the reply follower, and tool status. Tests build records with messageRecord in tests/helpers/records.ts, and the status rules are tested once.
2w ago
EmptyConversation.tsx
Finish the styling port by deleting main's stand-in sheet, its legacy layer, and every data-ported marker, so every element's look is its own utilities over the theme. The shared chat page, which leaned on the old sheet, gets the conversation's column with its title and who shared it above the messages, and its sign-in and loading states center on the panel. Copy's icon swap and the Jump to button stop scaling under reduced motion. The architecture doc describes the token theme and Streamdown and lists ADRs 0004 and 0005, the web UI spec no longer calls the app plain, the styling spec marks the port done and ticks the criteria checked, and the extending spec records what the styling port already delivered.
2w ago
HeaderFrame.tsx
A conversation on its way shows a few paragraphs of text lines, spaced as a reply's are, in place of one turn's bubble, name, and paragraph, which read as a chat with one message rather than messages loading. MessagesSkeleton replaces TurnSkeleton, and the shared page shows it under its title too. The page's frame, which the server sends, now holds the conversation header's row with a line for its title, so the lines start where they will once the conversation loads, rather than under the sidebar's toggle on a narrow screen. The header's row and its title on its way live in HeaderFrame.tsx, which the header and the frame share, so the frame stays free of the header's panels and menus. The web UI spec describes it.
2w ago
InstructionsPanel.tsx
Instructions in a chat's More menu opens a panel to edit the chat's own instructions, the systemPrompt of its info record, which the model follows in every reply there after the user's custom instructions. Saving sends them trimmed through the conversation's PATCH route and closes the panel, saving them unchanged only closes it, so the chat doesn't move up the list, and a failed save shows in the conversation's alert and keeps it open. Saving them blank removes systemPrompt from the record, as when the chat was created, since updateInfo now drops a field a patch sets to undefined. The menu item is disabled until the chat's info record has arrived from the local copy or the PDS, so the panel never starts empty over stored instructions, and the title waits for the same record, so a chat not yet on this device shows its skeleton rather than "New chat". Rename and instructions share one patch mutation, a disabled MenuItem shows as such, and the sharing panel's floating card is PanelCard, shared with the instructions panel, whose panelForm keeps a panel's form from taking room in the header's row, which had nudged its buttons aside. The web UI and styling specs describe it, with tests and an acceptance criterion.
2w ago
message-actions.tsx
The web app's message records are typed as the lexicon's MessageRecord, and a conversation's info as InfoRecord, from the local copy, which holds only records the server validated, through BranchMessage to the message, status, details, part, and tool views, so their reads need no casts. src/shared/message.ts holds what a status means, isPending and isFinished, which counts a status another client wrote that this app doesn't know as neither, along with a message's parts, whether it is encrypted, and who wrote it, in place of the checks written out in the message view, the message actions, the chat, the reply follower, and tool status. Tests build records with messageRecord in tests/helpers/records.ts, and the status rules are tested once.
2w ago
MessageView.tsx
The web app's message records are typed as the lexicon's MessageRecord, and a conversation's info as InfoRecord, from the local copy, which holds only records the server validated, through BranchMessage to the message, status, details, part, and tool views, so their reads need no casts. src/shared/message.ts holds what a status means, isPending and isFinished, which counts a status another client wrote that this app doesn't know as neither, along with a message's parts, whether it is encrypted, and who wrote it, in place of the checks written out in the message view, the message actions, the chat, the reply follower, and tool status. Tests build records with messageRecord in tests/helpers/records.ts, and the status rules are tested once.
2w ago
MoreActions.tsx
Port the conversation header, message actions, and scrolling from design-reference. The header spans the panel over a fade, its title renames the chat when clicked, and a quiet toolbar holds Jump to, a blurred layer stacking your messages that scrolls to the one picked and makes it glow, Share as a floating SharePanel, and More with Rename and Sync this chat, one panel at a time. Messages carry icon actions in main's keyed list: edit, copy, and details on yours, copy, regenerate, and More with Regenerate with and details on a reply, and Stop on a pending one, and the shared view shows details. Following the conversation glides down and tracks whether the reader is at the bottom without rerendering it. Floating menus share useDismiss and MenuItem.
2w ago
NewConversation.tsx
useChat, in features/conversation/chat.ts, is the open conversation as a chat, in the shape of the AI SDK's useChat without a message list of its own: the branch on screen and the messages behind it from the browser store, whether they are on their way or known empty, the reply running at the end of the branch, and send, regenerate, and stop, on top of useConversation, the reply follower, and useBranch. Every turn, from a send, an edit, or a regeneration, follows its reply, puts its new message on the branch, replacing m, and calls the view's onTurnStarted, with which ConversationView scrolls to the bottom, so regenerating now scrolls there too, as sending and editing did. useStartChat sends a new chat's first message, creating the chat and reusing it after a failed send. The composer only builds the message and hands it to send, so it no longer knows the chat's key, creates chats, or posts, and BranchComposer and NewConversation pass send through. useBranch absorbs useScrollToFocus, scrolling to a message the URL focuses while a picked one keeps the scroll, so the shared page now scrolls to a linked message too. useConversationActions keeps renaming, the instructions, and syncing, and the conversation's one alert shows whichever action or turn ran last. Its tests cover sending, regenerating, stopping, the running reply, starting a chat, and scrolling to a focus, and the composer's tests check the message it hands to send. The web UI spec describes the chat.
2w ago
PartsView.tsx
The web app's message records are typed as the lexicon's MessageRecord, and a conversation's info as InfoRecord, from the local copy, which holds only records the server validated, through BranchMessage to the message, status, details, part, and tool views, so their reads need no casts. src/shared/message.ts holds what a status means, isPending and isFinished, which counts a status another client wrote that this app doesn't know as neither, along with a message's parts, whether it is encrypted, and who wrote it, in place of the checks written out in the message view, the message actions, the chat, the reply follower, and tool status. Tests build records with messageRecord in tests/helpers/records.ts, and the status rules are tested once.
2w ago
PendingReply.tsx
The web app's message records are typed as the lexicon's MessageRecord, and a conversation's info as InfoRecord, from the local copy, which holds only records the server validated, through BranchMessage to the message, status, details, part, and tool views, so their reads need no casts. src/shared/message.ts holds what a status means, isPending and isFinished, which counts a status another client wrote that this app doesn't know as neither, along with a message's parts, whether it is encrypted, and who wrote it, in place of the checks written out in the message view, the message actions, the chat, the reply follower, and tool status. Tests build records with messageRecord in tests/helpers/records.ts, and the status rules are tested once.
2w ago
PromptStack.tsx
Port the conversation header, message actions, and scrolling from design-reference. The header spans the panel over a fade, its title renames the chat when clicked, and a quiet toolbar holds Jump to, a blurred layer stacking your messages that scrolls to the one picked and makes it glow, Share as a floating SharePanel, and More with Rename and Sync this chat, one panel at a time. Messages carry icon actions in main's keyed list: edit, copy, and details on yours, copy, regenerate, and More with Regenerate with and details on a reply, and Stop on a pending one, and the shared view shows details. Following the conversation glides down and tracks whether the reader is at the bottom without rerendering it. Floating menus share useDismiss and MenuItem.
2w ago
RecordDetails.tsx
The web app's message records are typed as the lexicon's MessageRecord, and a conversation's info as InfoRecord, from the local copy, which holds only records the server validated, through BranchMessage to the message, status, details, part, and tool views, so their reads need no casts. src/shared/message.ts holds what a status means, isPending and isFinished, which counts a status another client wrote that this app doesn't know as neither, along with a message's parts, whether it is encrypted, and who wrote it, in place of the checks written out in the message view, the message actions, the chat, the reply follower, and tool status. Tests build records with messageRecord in tests/helpers/records.ts, and the status rules are tested once.
2w ago
SiblingPicker.tsx
A change to the open conversation, such as a message arriving or a reply finishing, renders again only the messages it changed, rather than every message and its actions. BranchView gives each message's row only its record, which the local copy keeps the same object while unchanged, its place among its versions as numbers and the rkeys on either side, and functions that stay the same from render to render: useChat's stop and regenerate call the mutations' own stable functions, and useModels gives the same empty list until models arrive. The reply running at the end of the branch, whose Stop the composer offers instead, reaches the Stop actions through a RunningReply context rather than the operations every action gets, so a reply starting or finishing rerenders only those. The web UI spec says so, and a ConversationView test counts each message's renders as a reply finishes.
2w ago
Status.tsx
The web app's message records are typed as the lexicon's MessageRecord, and a conversation's info as InfoRecord, from the local copy, which holds only records the server validated, through BranchMessage to the message, status, details, part, and tool views, so their reads need no casts. src/shared/message.ts holds what a status means, isPending and isFinished, which counts a status another client wrote that this app doesn't know as neither, along with a message's parts, whether it is encrypted, and who wrote it, in place of the checks written out in the message view, the message actions, the chat, the reply follower, and tool status. Tests build records with messageRecord in tests/helpers/records.ts, and the status rules are tested once.
2w ago