refactor: rename workspace branding and update project overview
This commit is contained in:
@@ -1,54 +1,67 @@
|
||||
# Twitter Lite
|
||||
# Personal Workspace
|
||||
|
||||
Twitter Lite is a personal research deck for Twitter and Mastodon. Create
|
||||
multiple named decks and arrange up to six columns per deck. Each column is
|
||||
bound to a connection account, so platforms and multiple accounts work side by side.
|
||||
A personal workspace for understanding the day, choosing the next action,
|
||||
keeping context, and exploring information. Home brings together a short brief,
|
||||
flexible tasks and routines, messages, notes, and reading. A shared sidebar opens
|
||||
each dedicated workspace; its bottom avatar manages connected accounts.
|
||||
|
||||
## Scope
|
||||
The interface is in English. User-entered text and external posts retain their
|
||||
original language.
|
||||
|
||||
- Multiple deck profiles with creation, selection, renaming, and deletion
|
||||
- Twitter search, user timeline, and list columns with independent relay accounts
|
||||
## Workspaces and current status
|
||||
|
||||
| Workspace | Route | Current implementation |
|
||||
| --- | --- | --- |
|
||||
| Home | `/` | State-derived daily brief, flexible tasks and routines, and natural-language action proposals. Local prototype. |
|
||||
| Messages | `/support` | Sample conversations, editable replies, scheduling previews, and deferral. Sending is simulated. |
|
||||
| Journal | `/journal` | Notes, resume points, medication and weight entries, append-only corrections, and summary previews. Local prototype. |
|
||||
| Reader | `/inbox` | Searchable sample articles and a responsive article reader. Recommendation from activity history is not connected yet. |
|
||||
| Vitals | `/vitals` | Body and Environment dashboards with sample readings, charts, and simulated controls. No health or device connection. |
|
||||
| Research | `/deck` | Twitter and Mastodon columns, connected accounts, persistent decks, and optional Codex research chat. |
|
||||
|
||||
Home, Messages, and Journal share in-memory state across navigation; reloading
|
||||
resets it. The home input uses a deterministic English/Japanese interpreter to
|
||||
preview an action before confirmation, rather than an LLM. Research decks,
|
||||
connections, and chat history use server-side persistence.
|
||||
|
||||
See [execution support](docs/execution-support.md) and the
|
||||
[vitals preview](docs/vitals-preview.md) for prototype scope.
|
||||
|
||||
## Direction: personal context and execution
|
||||
|
||||
The intended connection with the separate private `life` / Rensheng repository
|
||||
is a loop: read relevant personal context, suggest useful next actions, record
|
||||
what happened, and compile meaningful outcomes back into durable context.
|
||||
This integration is planned; the app does not currently read or update life.
|
||||
|
||||
Rensheng would retain personal context, preferences, and the background of
|
||||
ongoing matters. This workspace would own its execution state and interaction
|
||||
history, with original conversations and appointments remaining in their source
|
||||
systems. Context should carry source references and verification dates.
|
||||
|
||||
Interests are queries over activity history, not settings the user must maintain.
|
||||
Routines provide optional guidance rather than a daily completion quota. Home
|
||||
should help the user choose a manageable next step, with room to defer, dismiss,
|
||||
or change direction.
|
||||
|
||||
## Research capabilities
|
||||
|
||||
- Multiple named decks with up to six columns each
|
||||
- Twitter search, user timelines, and lists with independent relay accounts
|
||||
- Mastodon OAuth, multiple accounts, search, user, list, and hashtag columns
|
||||
- Native X search syntax, Top/Latest ranking, and optional `filter:follows`
|
||||
- List discovery for the profile selected in the column editor
|
||||
- Column editing, ordering, deletion/undo, manual refresh, and cursor pagination
|
||||
- Platform logos in column headers distinguish Twitter and Mastodon at a glance
|
||||
- SQLite-backed shared decks, revision conflicts, and device-local active selection
|
||||
- Temporary views for AI exploration, with explicit save and temporary copies
|
||||
- List discovery for the account selected in the column editor
|
||||
- Column editing, ordering, deletion/undo, refresh, and cursor pagination
|
||||
- SQLite-backed shared decks, revision conflicts, and device-local selection
|
||||
- Temporary views for exploration, with explicit saving
|
||||
- Server-only encrypted Mastodon credentials and configurable Tailscale owner access
|
||||
- Read-only cards with original-post links, text, media, and quotes
|
||||
- Experimental WebMCP tools to manage decks and read or paginate their columns
|
||||
- Prototype: chat with a resident home Codex beside a live deck, reuse existing
|
||||
decks and write cited Markdown research reports on the host
|
||||
- Saved chat history with deck, account and citation restoration across devices
|
||||
and backend restarts
|
||||
- Read-only post cards with source links, text, media, and quotes
|
||||
- Experimental WebMCP tools to manage decks and read their columns
|
||||
- Optional resident Codex research chat, cited host-side Markdown reports,
|
||||
and saved conversations with deck, account, and citation restoration
|
||||
|
||||
The system interface is in English; user-entered text and external posts retain their original language.
|
||||
|
||||
`/` opens the daily home with a greeting, natural-language action proposals,
|
||||
flexible tasks/routines, messages, notes, and reading. `/deck` opens Twitter Lite.
|
||||
The shared sidebar also opens
|
||||
the article reader at `/inbox`. This UI-only prototype shows articles inferred
|
||||
from past reading, searches, and research, with no manual interest settings.
|
||||
Articles are fictional samples; search and article selection work locally.
|
||||
On mobile, the article list and reader switch views. The shared sidebar’s bottom
|
||||
avatar opens connected-account management from either workspace.
|
||||
|
||||
The shared sidebar also contains `/support` (communication support) and `/journal`
|
||||
(notes and health records). These use fictional conversations and local session
|
||||
state to preview reply review, scheduling, deferral, recording, and corrections.
|
||||
See [execution support](docs/execution-support.md) for scope and limitations.
|
||||
|
||||
`/vitals` is a separate [health/environment reference preview](docs/vitals-preview.md)
|
||||
with sample scores, body systems, environmental charts, and simulated controls.
|
||||
|
||||
The former SNS reader, search,
|
||||
list, user-profile, and conversation routes have been removed. Original-post
|
||||
links on post cards open their source site; SNS reply threads are not rendered
|
||||
inside the app.
|
||||
|
||||
Bluesky/Threads/Nostr connectors are not implemented yet. An external browser
|
||||
agent can also create and read decks through WebMCP.
|
||||
Original-post links open their source site; SNS reply threads are not rendered
|
||||
inside the app. Bluesky, Threads, and Nostr connectors are not implemented.
|
||||
|
||||
## Requirements and setup
|
||||
|
||||
@@ -129,10 +142,10 @@ accounts' lists before planning new searches, then read relevant list columns
|
||||
for evidence. Follow-ups can reuse catalogs already fetched in the conversation.
|
||||
Manual deck creation and column editing remain
|
||||
available. The backend handles tools even when the browser is closed.
|
||||
Use **新しいチャット** to leave the current conversation and start a fresh Codex
|
||||
Use **New chat** to leave the current conversation and start a fresh Codex
|
||||
thread with your next message. The open deck and host reports remain available.
|
||||
If a turn is running, stop it first. The conversation reset is shared across
|
||||
open devices. **チャット履歴** reopens saved conversations with their account
|
||||
open devices. **Chat history** reopens saved conversations with their account
|
||||
selection, deck and citations; the next message resumes the same Codex thread.
|
||||
SQLite stores conversation snapshots and the active conversation selection.
|
||||
Each turn is limited to 6 columns, 12 upstream requests shared between list
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Execution support prototype
|
||||
|
||||
The shared sidebar opens `/support` (Messages) and `/journal` (Journal), alongside
|
||||
Reader and Twitter Lite. These are UI-only prototypes with fictional contacts
|
||||
Reader and Research. These are UI-only prototypes with fictional contacts
|
||||
and in-memory state. Reloading resets the prototype state. Tasks, communication status, outcomes, and
|
||||
journal entries are shared between routes for the current browser session.
|
||||
No messages, calendar events, medical records, or Rensheng pages are written.
|
||||
@@ -41,7 +41,7 @@ Start here, Routines, and If there’s room. There are no fixed time slots.
|
||||
Tasks can be reordered, completed, deferred, restored, or followed as routine
|
||||
steps. Communications can be reviewed directly in a home dialog; changes also
|
||||
appear in `/support`. Journal entries and corrections appear on the home, and
|
||||
reader samples open for reading. Twitter Lite remains a separate work surface.
|
||||
reader samples open for reading. Research remains a separate work surface.
|
||||
|
||||
The input currently uses a deterministic English/Japanese mock interpreter, not an LLM.
|
||||
It proposes typed actions for task creation/status/reordering, routine steps,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
`/deck` renders the research workspace; `/` is the integrated daily home. A collapsible shadcn/ui
|
||||
sidebar based on `sidebar-09` shares its navigation rail with the article reader
|
||||
at `/inbox`. Within Twitter Lite, it switches between research chat and deck management.
|
||||
at `/inbox`. Within Research, it switches between research chat and deck management.
|
||||
Deck management switches decks and adds columns. The avatar at the bottom of the
|
||||
shared sidebar opens connected-account management in a dialog from either workspace.
|
||||
Up to six ordered columns scroll independently; only the active deck
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Shared storage and Mastodon OAuth
|
||||
|
||||
Twitter Lite runs as a single personal server behind Tailscale Serve. The
|
||||
Personal Workspace runs as a single personal server behind Tailscale Serve. The
|
||||
backend binds to loopback and accepts only the configured Tailscale login.
|
||||
Browser requests that change state must have the configured Origin. OAuth
|
||||
callbacks also pass the owner check; the browser must be able to reach the
|
||||
|
||||
@@ -41,7 +41,7 @@ test('selects articles and keeps the reader focused on their content', async ({
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('recovers from empty search and shares navigation with Twitter Lite', async ({
|
||||
test('recovers from empty search and shares navigation with Research', async ({
|
||||
page,
|
||||
a11y,
|
||||
}) => {
|
||||
@@ -61,7 +61,7 @@ test('recovers from empty search and shares navigation with Twitter Lite', async
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit interests', exact: true }),
|
||||
).toHaveCount(0)
|
||||
await page.getByRole('link', { name: 'Twitter Lite', exact: true }).click()
|
||||
await page.getByRole('link', { name: 'Research', exact: true }).click()
|
||||
await expect(page).toHaveURL(/\/deck$/)
|
||||
await page.getByRole('link', { name: 'Reader', exact: true }).click()
|
||||
await expect(page).toHaveURL(/\/inbox$/)
|
||||
|
||||
@@ -100,13 +100,13 @@
|
||||
let cfg = config.services.twitter-lite;
|
||||
in {
|
||||
options.services.twitter-lite = {
|
||||
enable = lib.mkEnableOption "Twitter Lite";
|
||||
enable = lib.mkEnableOption "Personal Workspace";
|
||||
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = mkTwitterLite pkgs;
|
||||
defaultText = lib.literalExpression "twitter-lite.packages.\${pkgs.system}.default";
|
||||
description = "Twitter Lite package to run.";
|
||||
description = "Personal Workspace package to run.";
|
||||
};
|
||||
|
||||
relayBaseUrl = lib.mkOption {
|
||||
@@ -143,13 +143,13 @@
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 3000;
|
||||
description = "Port on which Twitter Lite listens.";
|
||||
description = "Port on which Personal Workspace listens.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.services.twitter-lite = {
|
||||
description = "Twitter Lite";
|
||||
description = "Personal Workspace";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
|
||||
@@ -119,7 +119,7 @@ function Workspace({
|
||||
<SidebarInset className="min-w-0 overflow-hidden">
|
||||
<header className="flex h-12 shrink-0 items-center gap-2 border-b px-3">
|
||||
<SidebarTrigger aria-label="Toggle sidebar" />
|
||||
<span className="text-sm font-medium">Twitter Lite</span>
|
||||
<span className="text-sm font-medium">Research</span>
|
||||
<span className="ml-auto text-xs text-muted-foreground">
|
||||
Research workspace
|
||||
</span>
|
||||
|
||||
@@ -64,7 +64,7 @@ export function WorkspaceNavigation({
|
||||
active: pathname === '/inbox',
|
||||
},
|
||||
{
|
||||
title: 'Twitter Lite',
|
||||
title: 'Research',
|
||||
to: '/deck',
|
||||
icon: Search,
|
||||
active: pathname === '/deck',
|
||||
|
||||
@@ -548,7 +548,7 @@ export function HomePage() {
|
||||
))}
|
||||
</section>
|
||||
<Link to="/deck" className="home-deck-link">
|
||||
Explore in Twitter Lite
|
||||
Explore in Research
|
||||
<ArrowUpRight className="size-4" />
|
||||
</Link>
|
||||
</aside>
|
||||
|
||||
@@ -148,7 +148,7 @@ describe('home natural-language proposals', () => {
|
||||
['連絡を開いて', '/support'],
|
||||
['記録', '/journal'],
|
||||
['リーダーを開く', '/inbox'],
|
||||
['Twitter Liteに移動', '/deck'],
|
||||
['Researchに移動', '/deck'],
|
||||
])('recognizes navigation: %s', (input, to) => {
|
||||
expect(interpretAction(input, tasks, contacts)).toMatchObject({
|
||||
action: { type: 'navigate', to },
|
||||
|
||||
@@ -38,13 +38,13 @@ export function interpretAction(
|
||||
return { error: 'Please review this action in the relevant screen.' }
|
||||
text = text
|
||||
.replace(
|
||||
/^open (messages|journal|reader|twitter lite|deck)$/i,
|
||||
/^open (messages|journal|reader|research|deck)$/i,
|
||||
(_, name: string) =>
|
||||
({
|
||||
messages: '連絡',
|
||||
journal: '記録',
|
||||
reader: 'リーダー',
|
||||
'twitter lite': 'Twitter Lite',
|
||||
research: 'Research',
|
||||
deck: 'デッキ',
|
||||
})[name.toLowerCase()] ?? name,
|
||||
)
|
||||
@@ -83,7 +83,7 @@ export function interpretAction(
|
||||
連絡: '/support',
|
||||
記録: '/journal',
|
||||
リーダー: '/inbox',
|
||||
'Twitter Lite': '/deck',
|
||||
Research: '/deck',
|
||||
デッキ: '/deck',
|
||||
}
|
||||
const destination = text.replace(/(?:を開いて|を開く|に移動)$/, '')
|
||||
@@ -93,7 +93,7 @@ export function interpretAction(
|
||||
if (route)
|
||||
return {
|
||||
action: { type: 'navigate', to: route },
|
||||
label: `Open ${{ '/support': 'Messages', '/journal': 'Journal', '/inbox': 'Reader', '/deck': 'Twitter Lite' }[route]}`,
|
||||
label: `Open ${{ '/support': 'Messages', '/journal': 'Journal', '/inbox': 'Reader', '/deck': 'Research' }[route]}`,
|
||||
}
|
||||
|
||||
const note = text.match(/^(メモ|再開地点)[::\s]+(.+)$/s)
|
||||
|
||||
@@ -87,7 +87,7 @@ async function getApp(origin: string, database: AppDatabase, send: Send) {
|
||||
const registered = appSchema.parse(
|
||||
await (
|
||||
await formRequest(send, origin, '/api/v1/apps', {
|
||||
client_name: 'Twitter Lite',
|
||||
client_name: 'Personal Workspace',
|
||||
redirect_uris: redirectUri,
|
||||
scopes,
|
||||
})
|
||||
|
||||
@@ -19,7 +19,7 @@ export const Route = createRootRouteWithContext<RouterContext>()({
|
||||
content: 'width=device-width, initial-scale=1',
|
||||
},
|
||||
{ name: 'referrer', content: 'no-referrer' },
|
||||
{ title: 'Twitter Lite' },
|
||||
{ title: 'Personal Workspace' },
|
||||
],
|
||||
links: [
|
||||
{ rel: 'stylesheet', href: appCss },
|
||||
|
||||
Reference in New Issue
Block a user