319 lines
16 KiB
HTML
319 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Tasks - Roject</title>
|
|
<link rel="stylesheet" href="../_assets_/styles.css">
|
|
<link rel="stylesheet" href="../_assets_/nav.css">
|
|
<link rel="stylesheet" href="../_assets_/boards.css">
|
|
</head>
|
|
<body>
|
|
<div class="board-page">
|
|
|
|
<header>
|
|
<p class="date">Board</p>
|
|
<h1>Tasks</h1>
|
|
<p class="subtitle">Click a task title to expand or collapse its content.</p>
|
|
</header>
|
|
|
|
<div class="board">
|
|
|
|
<div class="lane">
|
|
<div class="lane-header">To Do</div>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Rojo Chat: LLM Tools</task-title>
|
|
<task-content>
|
|
Allow users to define and use tools in rojo-chat-panel. Tools extend the LLM
|
|
with callable functions (e.g. file read, web search, custom actions). The chat
|
|
panel should support declaring a tool set, passing tool definitions to the model,
|
|
handling tool-call responses, and feeding results back into the conversation.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Rojo Chat: Claude API</task-title>
|
|
<task-content>
|
|
Integrate the Claude API (Anthropic) as a provider option in rojo-chat-panel.
|
|
Allows testing and using Claude models alongside the existing OpenAI-compatible
|
|
provider. Covers API key configuration, model selection, and verifying streaming
|
|
and tool-use work end-to-end with the Anthropic SDK.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Mobile: editor layout too tall, chat input not reachable</task-title>
|
|
<task-content>
|
|
On mobile the overall editor layout is still too tall — panels overflow the
|
|
viewport and the chat input area is pushed out of view even after the
|
|
min-height: 0 fix on rojo-chat-panel. Needs a broader mobile layout pass
|
|
on the panel/section/editor structure.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Code syntax highlighting in rojo-chat (Highlight.js)</task-title>
|
|
<task-content>
|
|
Code blocks in assistant responses are rendered as plain text inside pre/code tags.
|
|
Integrate Highlight.js to apply syntax highlighting after markdown-it renders each
|
|
response chunk. Apply highlighting to all code blocks in the assistant bubble.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Rojo Character Editor</task-title>
|
|
<task-content>
|
|
Allow changing colors and selecting layers of a Rojo character from within
|
|
the rojo-settings-panel. Colors map to fill/stroke/both targets on SVG elements;
|
|
layers toggle visibility of named groups or swap between variants.
|
|
The appearance field (colors[], layers[]) is already in the settings.rojo schema.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Rojo Character Animation Box</task-title>
|
|
<task-content>
|
|
Animation system for emotional feedback during conversations.
|
|
Animations can be scripted (predefined sequences) or driven dynamically by
|
|
an LLM that emits emotion tags alongside its response. The animation box
|
|
plays character animations (idle, happy, thinking, surprised, etc.) in the
|
|
portrait area of the rojo-settings-panel and rojo-chat-panel.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>File tree: drag-and-drop move for files and directories</task-title>
|
|
<task-content>
|
|
Allow files and directories to be moved by dragging them within the file tree.
|
|
Dragging a file onto a directory moves it inside; dragging a directory onto another
|
|
directory moves the whole subtree. Use the existing file rename API
|
|
(POST /api/files/:projectId/rename) — moving is a rename to a new parent path.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Tab context menu on right-click (tabs and empty tab bar area)</task-title>
|
|
<task-content>
|
|
The tab ⋮ menu already works but should also open on right-click anywhere on
|
|
the tab bar — both on individual tabs and on the empty space to the right of the tabs.
|
|
Right-clicking a specific tab should also offer a "Close this tab" action directly.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Unauthenticated Landing Screen</task-title>
|
|
<task-content>
|
|
Unauthenticated users currently crash on dashboard components.
|
|
They should land on a screen that explains the app and shows a login link.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Replace browser confirm() in group editor and account delete</task-title>
|
|
<task-content>
|
|
The group editor and account delete button still use the browser confirm() dialog.
|
|
Replace with the custom <confirm-dialog> component already used elsewhere.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>MediaViewerPanel for images and PDFs</task-title>
|
|
<task-content>
|
|
Non-text files (images, PDFs) are visible in the file tree but not openable.
|
|
Add a MediaViewerPanel and register it in FileEditorRegistry for common media types.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Investigate Gitea webhook auto-deploy</task-title>
|
|
<task-content>
|
|
The webhook did not fire on the last two pushes to main. Check the Gitea
|
|
webhook delivery log for the response code from /api/deploy. Also run
|
|
journalctl -u roject -n 100 on the server to see whether the endpoint
|
|
was reached at all. Most likely causes: signature mismatch, wrong branch
|
|
ref, or the deploy command failing silently.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Roject: wire lookup-email for member storage migration</task-title>
|
|
<task-content>
|
|
The rokojori-auth side (POST /api/auth/lookup-email) is live.
|
|
Remaining Roject-side work:
|
|
|
|
— Add SERVICE_SECRET to Roject .env (must match rokojori-auth SERVICE_SECRET)
|
|
— In POST /api/projects/:id/members: call account.rokojori.com/api/auth/lookup-email,
|
|
receive the user ID, store member_id as the user ID instead of the email
|
|
— In source/server/projectAccess.ts change memberMatchesUser()
|
|
from member.member_id === user.email
|
|
to member.member_id === user.userId
|
|
— Write a one-off migration script: for each member row, call lookup-email
|
|
with the stored email and replace member_id with the returned user ID
|
|
</task-content>
|
|
</task-item>
|
|
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Switch Gitea webhook to dev branch</task-title>
|
|
<task-content>
|
|
The Gitea webhook currently triggers on pushes to main, redeploying on every commit.
|
|
Switch to a dev branch so main is the stable release target.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="blue hide-content">
|
|
<task-title>Ensure time is not depending on the user's clock</task-title>
|
|
<task-content>
|
|
JWT verification on the local server failed because the Windows client clock was
|
|
~65 minutes ahead of the production auth server clock. Any time-based logic that
|
|
compares client-side time against server-issued timestamps (JWT exp, token TTL,
|
|
session validity) is broken when clocks diverge.
|
|
|
|
Work to do:
|
|
— Audit all places where Date.now() / new Date() is used for security or
|
|
session decisions; replace with server-authoritative time where possible.
|
|
— On the auth side: use clockTolerance in jwt.verify as a configurable
|
|
escape hatch (JWT_CLOCK_TOLERANCE env var, already added for local dev).
|
|
— Write a developer guide covering: why user/client clock cannot be trusted,
|
|
how to use server time for all authoritative checks, how to diagnose clock
|
|
skew issues, and the JWT_CLOCK_TOLERANCE workaround for local dev.
|
|
— Consider syncing advice in the local dev setup docs (future local-dev task).
|
|
</task-content>
|
|
</task-item>
|
|
|
|
</div>
|
|
|
|
<div class="lane">
|
|
<div class="lane-header">In Progress</div>
|
|
|
|
<task-item class="yellow hide-content">
|
|
<task-title>rokojori-tunnel — Phase 2</task-title>
|
|
<task-content>
|
|
Production-deployed at tunnel.rokojori.com. Complete so far:
|
|
relay server, CRUD API, streaming WebSocket protocol (res_start / res_data / res_end),
|
|
HTTP proxy with SSE streaming, Electron Tunnel Agent (tray, login, tunnel list),
|
|
Roject browse-tunnels button, Roject LLM chat via tunnel, client-side chunk animation.
|
|
|
|
Remaining:
|
|
— Allowed users list enforcement (multi-user private access)
|
|
— Public access mode (no auth required on proxy route)
|
|
— GET /api/tunnels/available with ?purpose= filter surfaced in Roject provider picker
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="yellow hide-content">
|
|
<task-title>styles.rokojori.com — complete deployment</task-title>
|
|
<task-content>
|
|
Service is built and running manually via npm start on the server.
|
|
Remaining work:
|
|
— Set up systemd service (styles-rokojori.service) so it survives restarts
|
|
— Download Barlow from /add-fonts (weights 100, 400, 700, 900)
|
|
— Verify CORS is working for roject.rokojori.com font imports
|
|
— Add to the add-subdomain deployment guide as a reference example
|
|
</task-content>
|
|
</task-item>
|
|
|
|
|
|
</div>
|
|
|
|
<div class="lane">
|
|
<div class="lane-header">Done</div>
|
|
|
|
<task-item class="green hide-content">
|
|
<task-title>Auth Update — session refresh overhaul</task-title>
|
|
<task-content>
|
|
Full plan and rationale: <code>workspace/history/2026/08-August/02-Saturday/auth-update.page</code>.
|
|
Root cause was two bugs: browser tabs racing the same single-use refresh token
|
|
(rokojori-auth), and Electron never refreshing after its one-shot startup call.
|
|
|
|
— <b>Phase 1:</b> rokojori-auth refresh-token grace window. <code>db.ts</code>:
|
|
<code>RefreshToken</code> gained <code>usedAt</code>/<code>replacedBy</code>;
|
|
<code>markUsed()</code> marks-rotated instead of deleting; <code>create()</code> prunes
|
|
expired rows. <code>routes/auth.ts</code> <code>/api/auth/refresh</code>: first use rotates
|
|
+ calls <code>markUsed()</code>; same token reused within 10s (<code>REFRESH_GRACE_TTL</code>)
|
|
resolves to the same replacement pair instead of 401ing.
|
|
|
|
— <b>Phase 2:</b> <code>ActivityAnalyser</code> gained <code>OnVisibilityChange</code>.
|
|
New <code>source/auth/TokenUpdater.ts</code>: periodic 5-min timer + activity-triggered
|
|
pings to <code>GET /api/auth/me</code>, <code>EventSlot</code>-driven
|
|
<code>valid | refreshing | expired | network-error</code> state. Wired into
|
|
<code>editor-shell.ts</code>. Browser access token is <code>httpOnly</code>, so proactive
|
|
margin decision moved server-side: <code>jwtMiddleware</code> rotates within 15 min of
|
|
real expiry.
|
|
|
|
— <b>Phase 3:</b> <code>source/auth/GuardedCall.ts</code>: singleton wrapper with three
|
|
tiers — <b>user</b> (no retry, throw), <b>editor</b> (3 retries 1s/3s/8s, throw),
|
|
<b>silent</b> (same delays, never throws, <code>console.warn</code>). Pre-flight blocks
|
|
on <code>expired</code>; waits up to 6 s on <code>refreshing</code>. Wired into
|
|
<code>Editor.ts</code> and <code>editor-shell.ts</code> layout save/load.
|
|
|
|
— <b>Phase 4:</b> Web Locks leader election in <code>TokenUpdater.ts</code>. First tab
|
|
acquires <code>roject-token-updater-leader</code>, runs checks, broadcasts state via
|
|
<code>BroadcastChannel</code>. Followers listen and mirror state. Leader handoff is
|
|
automatic when the holder tab closes.
|
|
|
|
— <b>Phase 5:</b> Electron token updater in <code>electron/main.ts</code>. Server
|
|
clock offset from <code>Date</code> response header. JWT <code>exp</code> decoded
|
|
directly (no httpOnly cookie). <code>checkAndRefreshIfDue()</code> every 5 min,
|
|
refreshes within 15 min of expiry.
|
|
|
|
— <b>Phase 6:</b> <code>POST /api/auth/new-session</code> in
|
|
<code>rokojori-auth/routes/auth.ts</code>. <code>requireAuth</code>-guarded; mints a
|
|
fresh independent token pair via <code>issueTokenPair</code>.
|
|
|
|
— <b>Phase 7:</b> Session heartbeat in <code>electron/main.ts</code>. Running instance
|
|
writes <code>{ accessToken, timestamp }</code> every 10 s. New instance reads it on
|
|
startup; if ≤30 s old calls <code>POST /api/auth/new-session</code> to skip login.
|
|
Retired plaintext <code>last-password.txt</code> auto-login.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
<task-item class="green hide-content">
|
|
<task-title>Per-project per-device layout persistence</task-title>
|
|
<task-content>
|
|
Full tab tree (panels → sections → tab-containers → tabs including open files)
|
|
saved to .roject/layout-<deviceId>.json inside each project directory.
|
|
Remote projects: storage/<id>/root/.roject/. Local Electron: <localRoot>/.roject/.
|
|
Remote proxy (Electron opening a roject.rokojori.com project): centralized
|
|
layouts dir keyed by device + remoteProjectId.
|
|
deviceId in localStorage already differentiates Firefox, Chrome, and Electron.
|
|
Serialized format: { version, activePortraitPanel, panels: { left, center, right } }
|
|
where each panel has sections[], each section has tabContainers[], each
|
|
tab-container has tabs[] with { id, label, panelType, tag, openFile }.
|
|
FileEditorPanel interface extended with getCurrentFile(): string | null,
|
|
implemented in code-panel and page-editor-panel.
|
|
Restored on editor load; falls back to default layout if none saved.
|
|
makeResizeHandle() gained an optional onResize callback so inner handles trigger saves.
|
|
.roject/ filtered from both remote (storage.ts) and local (localFiles.ts) file trees.
|
|
Save triggers: panel resize, inner handle resize, tab click, file open,
|
|
split, close-container, add-panel, portrait panel switch.
|
|
</task-content>
|
|
</task-item>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<footer>
|
|
Roject — tasks
|
|
</footer>
|
|
|
|
</div>
|
|
<script>var NAV_ROOT = '../';</script>
|
|
<script src="../_assets_/nav-data.js"></script>
|
|
<script src="../_assets_/nav.js"></script>
|
|
<script>
|
|
document.querySelectorAll( 'task-title' ).forEach( title =>
|
|
{
|
|
title.addEventListener( 'click', () =>
|
|
{
|
|
title.closest( 'task-item' ).classList.toggle( 'hide-content' );
|
|
} );
|
|
} );
|
|
</script>
|
|
</body>
|
|
</html>
|