diff --git a/CHANGELOG.md b/CHANGELOG.md index ffa59fa1..be2bca13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.4] - 2026-06-18 + +### Added + +- ๐ฑ **Full PWA support.** cptr can now be installed as a standalone app on phones, tablets, and desktops. Includes offline caching, an offline fallback page, home screen shortcuts (New Chat, Open Workspace, New Note, New Terminal, Search), and a service worker that keeps static assets available when the server is unreachable. +- ๐ค **Share target.** Share files, text, or links from other apps directly into cptr. On mobile, use the system share sheet to send content straight to a chat. +- ๐ **File handling.** Opening supported file types (code, documents, images) with cptr now imports them into your workspace with a folder picker. +- ๐ **Context usage indicator.** The chat panel now shows how full the context window is, so you can see at a glance how much room is left before compaction kicks in. +- ๐๏ธ **Workspace picker for imports.** When importing shared files or opening files from outside the app, a workspace picker lets you choose where to save them. +- ๐ง **PWA settings tab.** A new tab in Settings shows your install status, lets you check for service worker updates, and clear the offline cache. +- ๐ฅ๏ธ **Status modal.** A new status indicator in the chat panel shows server connection state and context usage at a glance. + +### Changed + +- ๐ **Perplexity base URL is now configurable.** You can point the Perplexity search provider at a custom endpoint (like a LiteLLM proxy) from Settings or via the `PERPLEXITY_BASE_URL` environment variable. +- ๐ค **Simplified default system prompt.** Removed the instruction that told the AI to always create a plan before acting. The AI now helps directly unless you ask it to plan. +- ๐ **File uploads no longer overwrite existing files.** Uploading a file with the same name as an existing one now automatically adds a number suffix instead of replacing it. +- ๐ **New translation keys.** Added PWA, share, file handling, and status labels across supported languages. + ## [0.5.3] - 2026-06-17 ### Added diff --git a/README.md b/README.md index dc50b34d..3678f291 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ `cptr` (short for "computer") runs on your machine and serves your whole computer (files, terminal, editor, git) to any browser. It literally is your computer. -Use it from your phone, tablet, laptop, another computer, or the machine it's running on. Designed to feel native on every screen. Plug in an AI that can actually read, write, and run things on your machine, or bring your favourite terminal agent. Terminal multiplexer, parallel AI agents, full workstation, one tool, any device. +Use it from your phone, tablet, laptop, another computer, or the machine it's running on. Designed to feel native on every screen. Plug in an AI that can actually read, write, and run things on your machine, or bring your favourite terminal agent. Terminal multiplexer, parallel AI agents, full workstation, one tool, your computer, any device. ## Install @@ -136,6 +136,9 @@ Close the tab. Come back tomorrow on any device. Everything is where you left it Life is short. Touch grass. Read our [Manifesto](MANIFESTO.md). +> Help us build open, empowering tools that put AI in people's hands. +> [We're hiring](http://careers.openwebui.com/). + ## Docker Run cptr with Docker: diff --git a/cptr/app.py b/cptr/app.py index d894cfad..4e416550 100644 --- a/cptr/app.py +++ b/cptr/app.py @@ -329,11 +329,14 @@ async def pwa_manifest(): name = f"cptr @ {hostname}" if hostname else "cptr" return { + "id": "/", "name": name, "short_name": "cptr", "description": f"Your computer, from anywhere. v{version}", "start_url": "/", + "scope": "/", "display": "standalone", + "display_override": ["window-controls-overlay", "standalone"], "orientation": "any", "background_color": "#000000", "theme_color": "#000000", @@ -348,6 +351,112 @@ async def pwa_manifest(): }, ], "categories": ["developer", "productivity", "utilities"], + "launch_handler": {"client_mode": ["navigate-existing", "auto"]}, + "handle_links": "preferred", + "note_taking": {"new_note_url": "/?intent=newNote"}, + "shortcuts": [ + { + "name": "New Chat", + "short_name": "Chat", + "url": "/?intent=newChat", + "icons": [{"src": "/icon-192.png", "sizes": "192x192"}], + }, + { + "name": "Open Workspace", + "short_name": "Workspace", + "url": "/?intent=openWorkspace", + "icons": [{"src": "/icon-192.png", "sizes": "192x192"}], + }, + { + "name": "New Note", + "short_name": "Note", + "url": "/?intent=newNote", + "icons": [{"src": "/icon-192.png", "sizes": "192x192"}], + }, + { + "name": "New Terminal", + "short_name": "Terminal", + "url": "/?intent=newTerminal", + "icons": [{"src": "/icon-192.png", "sizes": "192x192"}], + }, + { + "name": "Search", + "short_name": "Search", + "url": "/?intent=search", + "icons": [{"src": "/icon-192.png", "sizes": "192x192"}], + }, + ], + "share_target": { + "action": "/?intent=share", + "method": "POST", + "enctype": "multipart/form-data", + "params": { + "title": "title", + "text": "text", + "url": "url", + "files": [ + { + "name": "files", + "accept": [ + "text/*", + "application/pdf", + "image/*", + ".md", + ".py", + ".js", + ".ts", + ".tsx", + ".jsx", + ".svelte", + ".json", + ".yaml", + ".yml", + ".toml", + ".rs", + ".go", + ], + } + ], + }, + }, + "file_handlers": [ + { + "action": "/?intent=importFiles", + "accept": { + "text/*": [ + ".txt", + ".md", + ".py", + ".js", + ".ts", + ".tsx", + ".jsx", + ".svelte", + ".css", + ".html", + ".json", + ".yaml", + ".yml", + ".toml", + ".rs", + ".go", + ".java", + ".c", + ".cpp", + ".h", + ".hpp", + ".sh", + ], + "application/pdf": [".pdf"], + "image/*": [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"], + }, + } + ], + "protocol_handlers": [ + {"protocol": "web+cptr", "url": "/?intent=%s"}, + ], + "prefer_related_applications": False, + "related_applications": [], } diff --git a/cptr/frontend/src/app.html b/cptr/frontend/src/app.html index c16e7799..ec5e73b4 100644 --- a/cptr/frontend/src/app.html +++ b/cptr/frontend/src/app.html @@ -12,10 +12,12 @@ + + %sveltekit.head%
diff --git a/cptr/frontend/src/lib/apis/chat.ts b/cptr/frontend/src/lib/apis/chat.ts index ecd0afbb..e9deb291 100644 --- a/cptr/frontend/src/lib/apis/chat.ts +++ b/cptr/frontend/src/lib/apis/chat.ts @@ -28,9 +28,18 @@ export interface ChatInfo { is_active?: boolean; } +export interface ContextUsage { + tokens: number; + estimated_tokens: number; + threshold: number; + percent: number; + source: 'estimated'; +} + export interface ChatDetail { chat: ChatInfo; messages: ChatMessageRow[]; + context_usage?: ContextUsage | null; } export interface SendMessageResult { @@ -48,6 +57,16 @@ export interface ChatSendParams { voice_mode?: boolean; } +export interface CompactChatResult { + ok: boolean; + compacted: boolean; + reason?: string; + dropped_messages?: number; + kept_messages?: number; + summary_chars?: number; + context_usage?: ContextUsage | null; +} + // โโ Queries โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ export const getChats = ( @@ -61,7 +80,10 @@ export const getChats = ( `/api/chats?workspace=${encodeURIComponent(workspace)}&limit=${limit}&offset=${offset}&sort_by=${sortBy}&sort_dir=${sortDir}` ); -export const getChat = (chatId: string) => fetchJSON- {$t('admin.audio.voiceMemosHint')} -
++ {$t('admin.audio.voiceMemosHint')} +
- -- {transcribeEnabled - ? $t('admin.audio.transcribeOnHint') - : $t('admin.audio.transcribeOffHint')} -
+ ++ {transcribeEnabled + ? $t('admin.audio.transcribeOnHint') + : $t('admin.audio.transcribeOffHint')} +
-+ {quality === 'high' + ? $t('admin.audio.qualityHintHigh') + : quality === 'medium' + ? $t('admin.audio.qualityHintMedium') + : $t('admin.audio.qualityHintLow')} +
- {quality === 'high' - ? $t('admin.audio.qualityHintHigh') - : quality === 'medium' - ? $t('admin.audio.qualityHintMedium') - : $t('admin.audio.qualityHintLow')} -
-+ {$t('admin.audio.sttHint')} +
- {$t('admin.audio.sttHint')} -
-- {$t('admin.audio.ttsEnabledHint')} -
- -- {$t('admin.audio.ttsAutoStreamHint')} -
-