diff --git a/README.md b/README.md index bce796a..8203579 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/execution-support.md b/docs/execution-support.md index dbb085f..020a402 100644 --- a/docs/execution-support.md +++ b/docs/execution-support.md @@ -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, diff --git a/docs/research-decks.md b/docs/research-decks.md index 3755789..0d09031 100644 --- a/docs/research-decks.md +++ b/docs/research-decks.md @@ -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 diff --git a/docs/storage-and-oauth.md b/docs/storage-and-oauth.md index d8e15e6..a2000a8 100644 --- a/docs/storage-and-oauth.md +++ b/docs/storage-and-oauth.md @@ -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 diff --git a/e2e/integrations/inbox.test.ts b/e2e/integrations/inbox.test.ts index 9e8fc7d..1444130 100644 --- a/e2e/integrations/inbox.test.ts +++ b/e2e/integrations/inbox.test.ts @@ -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$/) diff --git a/flake.nix b/flake.nix index 2dd62aa..b0b0151 100644 --- a/flake.nix +++ b/flake.nix @@ -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 = { diff --git a/src/components/app-shell.tsx b/src/components/app-shell.tsx index 2a9b5ab..e700b77 100644 --- a/src/components/app-shell.tsx +++ b/src/components/app-shell.tsx @@ -119,7 +119,7 @@ function Workspace({
- Twitter Lite + Research Research workspace diff --git a/src/components/workspace-navigation.tsx b/src/components/workspace-navigation.tsx index a75f0ea..ac0ed86 100644 --- a/src/components/workspace-navigation.tsx +++ b/src/components/workspace-navigation.tsx @@ -64,7 +64,7 @@ export function WorkspaceNavigation({ active: pathname === '/inbox', }, { - title: 'Twitter Lite', + title: 'Research', to: '/deck', icon: Search, active: pathname === '/deck', diff --git a/src/features/home/home-page.tsx b/src/features/home/home-page.tsx index babdcb8..8ffcc08 100644 --- a/src/features/home/home-page.tsx +++ b/src/features/home/home-page.tsx @@ -548,7 +548,7 @@ export function HomePage() { ))} - Explore in Twitter Lite + Explore in Research diff --git a/src/features/home/interpret-action.test.ts b/src/features/home/interpret-action.test.ts index 7126b3b..4ea4310 100644 --- a/src/features/home/interpret-action.test.ts +++ b/src/features/home/interpret-action.test.ts @@ -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 }, diff --git a/src/features/home/interpret-action.ts b/src/features/home/interpret-action.ts index 181ee40..9b0eead 100644 --- a/src/features/home/interpret-action.ts +++ b/src/features/home/interpret-action.ts @@ -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) diff --git a/src/features/mastodon/oauth.server.ts b/src/features/mastodon/oauth.server.ts index 1b740b6..b6c782f 100644 --- a/src/features/mastodon/oauth.server.ts +++ b/src/features/mastodon/oauth.server.ts @@ -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, }) diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 55000fa..aa95695 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -19,7 +19,7 @@ export const Route = createRootRouteWithContext()({ content: 'width=device-width, initial-scale=1', }, { name: 'referrer', content: 'no-referrer' }, - { title: 'Twitter Lite' }, + { title: 'Personal Workspace' }, ], links: [ { rel: 'stylesheet', href: appCss },