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
6 folders
·
4 files
at
rate-limits
hadsie.com
·
Retry busy providers. Don't retry spent keys. Add messaging for users.
·
2w ago
dead7601
features
Retry busy providers. Don't retry spent keys. Add messaging for users.
2w ago
parts
A reply's sources sit behind their count, which opens like a dropdown onto a numbered list with one source to a line, rather than badges wrapped side by side under a count. The count is a details summary with a chevron, and it starts closed, so the sources stay out of the reply's way until asked for. Each row is as wide as its link, in the well's grey, so its hover tint covers the link rather than the line. The list no longer folds past seven, since opening the count already asks for every source; a web search's results still fold. The web UI spec says so, and the message test checks the sources start closed and open to show all ten.
2w ago
routes
The sign-in field suggests accounts as you type a handle, from Bluesky's public searchActorsTypeahead, which the browser asks itself without a referrer after a 150 ms pause, skipping a DID or PDS URL. Each suggestion shows the account's avatar thumbnail, display name, and handle, and picking one by click or with the arrow keys and Enter fills in its handle, with its avatar in place of the @ until the field is edited, while Enter with nothing picked signs in as before. The panel shows skeleton rows once a first search takes the quiet wait, keeps the last accounts with a spinner in the field while a later one loads, and says when nothing matches or the search failed, with a hint to type the full handle, and screen readers hear how many accounts were found. The login page preconnects to the typeahead and Bluesky's image CDN, the field turns off the browser's own autocomplete, and Avatar is its own component, shared with the account card. The auth spec describes it, with tests and acceptance criteria.
2w ago
shared
A stopped reply says so with a small amber Stopped badge, carrying the Stop button's square, under where it broke off, rather than a loose line of muted text that read like the end of the reply. The badge is the shared Badge with a new warning tone, from the highlight tokens, so it stands apart from the grey source chips. The web UI spec says so, and the message test looks for the badge.
2w ago
store
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
styles
Selected text is a calm blue, its own selection token, rather than the pale amber it shared with search matches and the Stopped badge. Blue is the one hue nothing else in the app means anything by, so a selection never reads as a match, a notice, or the green of an action. Text keeps its own ink on it: body text clears 7:1 and muted text 4.5:1 in both modes.
2w ago
logo.svg
Load less and rerender less. The new-chat page no longer downloads the Markdown renderer: the conversation and the search results load with React.lazy, the chat route's loader starts the conversation's download, the new-chat page starts it once it shows, and opening the search starts the results', so what / downloads drops from 335 KB to 184 KB with brotli. Streamdown imports rehype-raw for its defaults, which markdown.tsx leaves out, so vite.config.ts aliases it to an empty stand-in in parts/no-raw.ts, keeping parse5 out of the build and the Markdown chunk from 163 KB to 71 KB gzipped. The logo's paths are rounded to two decimals, from 10.0 KB to 6.6 KB gzipped. The chat routes' loader starts the models, attachment types, and profile queries without waiting for them, and their head preconnects to the avatar's CDN. attachmentTypesQuery moves to its own module, so the loader doesn't bring the store's hooks into the entry. Pointing at or focusing a link preloads its route, the query cache deciding what is stale.
2w ago
router.tsx
Load less and rerender less. The new-chat page no longer downloads the Markdown renderer: the conversation and the search results load with React.lazy, the chat route's loader starts the conversation's download, the new-chat page starts it once it shows, and opening the search starts the results', so what / downloads drops from 335 KB to 184 KB with brotli. Streamdown imports rehype-raw for its defaults, which markdown.tsx leaves out, so vite.config.ts aliases it to an empty stand-in in parts/no-raw.ts, keeping parse5 out of the build and the Markdown chunk from 163 KB to 71 KB gzipped. The logo's paths are rounded to two decimals, from 10.0 KB to 6.6 KB gzipped. The chat routes' loader starts the models, attachment types, and profile queries without waiting for them, and their head preconnects to the avatar's CDN. attachmentTypesQuery moves to its own module, so the loader doesn't bring the store's hooks into the entry. Pointing at or focusing a link preloads its route, the query cache deciding what is stale.
2w ago
routeTree.gen.ts
Each API key has its own page at /settings/api-keys/<id>, opened from its row, which the list now shows with how many models the key covers. Under a link back to the list, the page is titled with the key's name over its provider and base URL, and has a card for each model with Vision, Thinking, and Tools switches, the same as an admin model's, and one Save, so a user's own models can say what they can do. PATCH /api/credentials/:id replaces a key's models, refusing an empty list or a model listed twice, and leaves the key itself alone. The capability list moves to features/models for both pages to share, SectionNav takes an exact flag per section, and LinkItem fades a row on its way out.
2w ago
start.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