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
/
shared
2 folders
·
32 files
at
main
pouriade.com
·
Open the Add a key form from a button
·
3d ago
b1f9c0f6
schema-fields
Carry line height and tracking in the type scale
3d ago
ui
Open the Add a key form from a button
3d ago
api.ts
Consolidate the web app's session hooks and simplify the conversation hooks. Signing out and ending a session share signOut and toLogin in features/auth/sign-out.ts, used by useSignOut, which now returns its mutation, the viewer's sign-out, and endSession, keeping their difference: signing out shows a failed delete of the local copy, while an ended session logs it and leaves. StoreProvider reports the store's sync finding the session ended through the same listeners as a request's 401, so one useEndSessionOnUnauthorized in the signed-in layout handles both, and the chat layout keeps no Effect. A test covers it. The store's query hooks share useActiveStore, session.tsx becomes session.ts with Me an interface, replyStreamQuery moves to the conversation's queries.ts, useBranch and useReplyStream drop the useCallbacks React Compiler now covers, and useStickToBottom returns a callback ref that follows the content's scrolling parent while it is mounted, in place of an Effect bound to a ref once.
3w ago
cn.ts
Guard the design system's scales in the theme test
3d ago
ErrorPage.tsx
Snap spacing to whole and half steps
3d ago
errors.ts
Group the web app's code by feature. Each folder in src/features (auth, chat-list, conversation, sharing, models, settings, admin) holds that feature's pages, layouts included, components, hooks, lib, and queries, and the routes stay thin, rendering a feature's page. Page files are named after the component they export. The query factories split from src/queries.ts into each feature's queries.ts, leaving staleTime in src/shared, which also holds the API client, errors, search params, and the schema forms that admin and settings share. inheritedModel moves from the models module to the conversation's branch module, so models no longer depends on the conversation. Tests mirror the new tree, and the specs, the architecture doc, and CLAUDE.md name the new paths.
3w ago
form.tsx
Open the Add a key form from a button
3d ago
icons.tsx
Add shared Input and TextArea fields
1w ago
keys.ts
Split the handle field's suggestions into their own component
3d ago
lines.ts
Split the web app's leaf components into smaller ones that return early. MessageView keeps the message's layout, and drawing parts moves to src/parts, as the extending spec lays out: markdown.tsx holds the renderer with its exported plugin lists and components, each part kind has a view taking { part, record, blobUrl }, and partViews in parts/index.ts maps kinds to them, matching only its own keys. Reasoning is one component, shared with the streamed reply, and SiblingPicker, Status, and StreamingReply have their own files. Source links are keyed like other parts, so two with the same URL no longer share a key. SchemaFields renders a SchemaField per property, with inputKind picking the control, and shares splitLines with RoleAdmin. SearchResults returns early from ResultList and ResultLabel in place of nested ternaries. SectionNav renders the admin and settings section links, ViewerSignOut moves from the login route to features/auth, and ModelSelect's props extend the select's directly. SchemaFields and RolePicker return a fragment rather than an array, since React Compiler silently skips a component that returns an array.
3w ago
message.ts
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
plural.ts
Port the chat sidebar from design-reference. The server answers GET /api/me/profile with the display name and Bluesky's CDN avatar thumbnail, read from the PDS on each request, and the browser store lists each chat's pending reply and newest complete reply and records when the index last synced. The sidebar puts New chat beside a search toggle, the tags as a folding filter, the chats under date headings with a dot for a reply in progress or one not yet seen on this device, a menu on each to rename, tag, or delete it in place, search results grouped by chat with their Markdown inline, and the account at the foot with a sync dot and a menu of where the chats live, their sync, Settings, Admin, and Sign out. New chat still creates the chat until the empty composer arrives.
2w ago
queries.ts
Group the web app's code by feature. Each folder in src/features (auth, chat-list, conversation, sharing, models, settings, admin) holds that feature's pages, layouts included, components, hooks, lib, and queries, and the routes stay thin, rendering a feature's page. Page files are named after the component they export. The query factories split from src/queries.ts into each feature's queries.ts, leaving staleTime in src/shared, which also holds the API client, errors, search params, and the schema forms that admin and settings share. inheritedModel moves from the models module to the conversation's branch module, so models no longer depends on the conversation. Tests mirror the new tree, and the specs, the architecture doc, and CLAUDE.md name the new paths.
3w ago
ref.ts
Add shared Input and TextArea fields
1w ago
response.ts
Group the web app's code by feature. Each folder in src/features (auth, chat-list, conversation, sharing, models, settings, admin) holds that feature's pages, layouts included, components, hooks, lib, and queries, and the routes stay thin, rendering a feature's page. Page files are named after the component they export. The query factories split from src/queries.ts into each feature's queries.ts, leaving staleTime in src/shared, which also holds the API client, errors, search params, and the schema forms that admin and settings share. inheritedModel moves from the models module to the conversation's branch module, so models no longer depends on the conversation. Tests mirror the new tree, and the specs, the architecture doc, and CLAUDE.md name the new paths.
3w ago
route-states.tsx
Keep the form kit out of the entry chunk
1w ago
search-params.ts
Merge web-rebuild: the web app on TanStack Start. Port main's no-models composer block, role and model summary pages, API keys and Cron pages into its feature folders
3w ago
SectionNav.tsx
Snap spacing to whole and half steps
3d ago
SidebarLayout.tsx
Split SidebarLayout into the chats and settings layouts
3d ago
site.ts
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
tags.ts
Edit chat tags as chips in a tags input
3d ago
theme.ts
Layer colors into scales, roles, and components
3d ago
time.ts
Port the admin area from design-reference. Users is a card to add a person over those waiting to sign in, then a search over a list of accounts, each with its initial, its DID, storage, and last activity, its roles as chips with a picker to add one, and its access at the end, where Suspend asks for a reason first. Roles and plugins are lists whose rows open their pages, which link back above their titles, a plugin's numbered in load order with its status and arrows to move it. A role's page holds its members, then its details with hosts and domains in a mono field. Access picks a registration mode from choices, with invite roles as chips that toggle. Models picks the default by a click anywhere on its row. A plugin's page holds its settings, a list of its provider's models, and a card to tick or type more. A model's capabilities are switches. Removing a role, plugin, or model, and revoking a key, confirm in place, and each sits in a red card. An issued key shows once in a green note with Copy. Cron shows the last run with a badge and the crontab line to copy. General, Turns, and Sync choose their cards per route, and the settings schemas gain clearer titles, descriptions, and units, which SchemaFields shows after a number.
2w ago
touch.ts
A Select given searchLabel opens a list of eight options or more, about as many as it shows before scrolling, with a search box named by it, and the model select gives "Search models", which finds a model by its name, its ID, or its provider through the options' new keywords. The box takes the focus as the list opens, except on a touch screen, where it waits for a tap so its keyboard doesn't cover the list. Typing narrows the options to those holding every word typed, in any case, moves to the first that matches, and says "No matches" through a status that is there from the start, so screen readers hear it. The arrow keys, Page Up, Page Down, and Enter work from the box while Home, End, and Space stay the field's, Escape closes the list and Tab moves on, each from the button, which takes the focus back as after a pick, and a letter typed on the closed button opens the list searching for it. An option's id comes from its place among every option, so the active id changes with each move and screen readers and the scroll into view follow it. The list's heading and search box sit over the options, which alone scroll, and usePlacement measures the room the reader can see, following the visual viewport as an on-screen keyboard comes and goes. touchOnly is shared with the composer from shared/touch.ts. The styling and web UI specs describe it, with tests and an acceptance criterion.
2w ago
useDebounced.ts
Group the web app's code by feature. Each folder in src/features (auth, chat-list, conversation, sharing, models, settings, admin) holds that feature's pages, layouts included, components, hooks, lib, and queries, and the routes stay thin, rendering a feature's page. Page files are named after the component they export. The query factories split from src/queries.ts into each feature's queries.ts, leaving staleTime in src/shared, which also holds the API client, errors, search params, and the schema forms that admin and settings share. inheritedModel moves from the models module to the conversation's branch module, so models no longer depends on the conversation. Tests mirror the new tree, and the specs, the architecture doc, and CLAUDE.md name the new paths.
3w ago
useDismiss.ts
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
useDocumentTitle.ts
An open chat's browser tab shows its title before the app name, as "Tile quotes · SCN Chat", and so does a shared chat with a title, through useDocumentTitle, since the title lives in the browser store or the shared chat's query, which the router's head never sees. Until the title is known the tab keeps the route's, an untitled chat shows "New chat", and leaving the chat puts back the title it replaced unless the router has set another since, as for Settings. The web UI spec describes it, with tests and an acceptance criterion.
2w ago
useInstantThemeSwitch.ts
Lay the styling foundation on Tailwind v4. styles/theme.css holds every token from design-reference under Tailwind's namespaces, with Tailwind's own palette, scales, and breakpoints cleared so utilities can only name the theme, and styles/base.css styles only the document. Main's stand-in look moves to styles/legacy.css, in a layer between the base and the utilities, scoped to stop at any area marked data-ported and restoring the browser defaults Preflight removes, until the port is done. A System, Light, or Dark choice is kept on the device and applied by a script in the head before the first paint, and useInstantThemeSwitch switches without smearing for the system and other tabs. The icons come over from design-reference taking a class, the logo draws inline in theme colors from the favicon's file, cn merges classes knowing the theme's names, Biome sorts classes, and a test keeps raw colors and lengths out of components.
2w ago
useNow.ts
Menus share one popover hook, focus moves without effects, and the ticking clocks share useNow.
1w ago
usePlacement.ts
A Select given searchLabel opens a list of eight options or more, about as many as it shows before scrolling, with a search box named by it, and the model select gives "Search models", which finds a model by its name, its ID, or its provider through the options' new keywords. The box takes the focus as the list opens, except on a touch screen, where it waits for a tap so its keyboard doesn't cover the list. Typing narrows the options to those holding every word typed, in any case, moves to the first that matches, and says "No matches" through a status that is there from the start, so screen readers hear it. The arrow keys, Page Up, Page Down, and Enter work from the box while Home, End, and Space stay the field's, Escape closes the list and Tab moves on, each from the button, which takes the focus back as after a pick, and a letter typed on the closed button opens the list searching for it. An option's id comes from its place among every option, so the active id changes with each move and screen readers and the scroll into view follow it. The list's heading and search box sit over the options, which alone scroll, and usePlacement measures the room the reader can see, following the visual viewport as an on-screen keyboard comes and goes. touchOnly is shared with the composer from shared/touch.ts. The styling and web UI specs describe it, with tests and an acceptance criterion.
2w ago
usePopover.ts
Move through the account menu with the arrow keys
1w ago
useServiceWorker.ts
The app can be installed from Chrome, Edge, and Android, or added to an iPhone's home screen, and opens in its own window. The server builds /manifest.webmanifest from the admin area's name, the icons are drawn from logo.svg on the sign-in page's dark tile by pnpm --filter @scn-chat/web icons, and theme-color tags follow the scheme fixed on the device. A hand-written service worker, stamped per build by a Vite plugin, keeps only the hashed /assets files and an offline page, leaves the API, sign-in, and XRPC to the browser, and drops the previous build's cache, so no chat content is cached (ADR 0006). The spec is specs/installable-app.md.
1w ago
useSidebar.ts
A collapsed sidebar stays collapsed through a reload, never showing open first. The head's script, beside the theme's, reads the stored choice and marks the root with data-sidebar-collapsed before the first paint, which the layout's styles already follow from any ancestor, and toggling keeps that attribute current. The server's frame, which can't know the choice, needs no change on hydrating, so it no longer turns off the slide.
2w ago
version.ts
The Settings sidebar shows the app's version under its links, and every page names it in a version meta tag, so a deployment's release can be checked, even without signing in. The version comes from the root package.json through shared/version.ts.
1w ago