Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- macOS close traffic light shows × on hover and a pressed state
- Clicking the pet again reopens chat beside the icon instead of restoring the last screen position
- Chat window height no longer snaps when starting a new conversation or sending the first message; resize stays bottom-anchored and eases over 360ms
- Model and connector menus stay inside the chat window and scroll instead of growing the OS window
- Queued follow-up turns no longer mark the finished reply as “连接意外断开”
- Saving settings now logs in and stores the token; testing connection also stores the password
- Chat can silently log in with the saved password when the access token is missing
- HTTP errors show the server message (e.g. “认证失败。”) instead of raw JSON
- Connection failures in chat offer a retry button
- Retrying an assistant turn reuses the original text, attachments, and model
- Settings leftover notices no longer linger on other tabs; shortcut hint and mascot labels are clearer
- Tray “检查更新” spacing and pet menu “与 Octop 对话” wording

### Added

- Model and agent popovers grow with the chat window so a tall window can show the full list
- Empty chat shows a gray logo in the message area
- Agent picker uses the same popover as the model list
- Setting to keep chat and settings visible when clicking another app (on by default)
- Settings tabs: 常规 (connection + mascot), 窗口, 快捷键, 关于
- `hooks/useChatController.ts`, `hooks/useWindowChrome.ts` — chat/settings window logic extracted from UI
- `lib/octopTypes.ts`, `lib/tauriWindowApi.ts`, `lib/chatHelpers.ts` — clearer module boundaries
- `components/ChatChrome.tsx`, `components/ChatResizeChrome.tsx`
Expand All @@ -20,6 +40,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Chat window starts at 400px and stays resizable; empty compact state fills the window instead of shrink-wrapping
- Sending a message no longer changes chat window height
- Narrow chat windows (~300px) keep the send button on-screen; toolbar chips shrink and wrap
- Debug builds store credentials in a local app-data file so macOS Keychain does not prompt on every rebuild
- Chat and settings close buttons follow the OS: traffic-light dot on the left on macOS, × on the right on Windows and Linux
- Settings window hides after a successful save
- `ChatWindow.tsx` slimmed to layout shell (~90 lines); logic in `useChatController`
- `SettingsWindow` uses shared auto-fit + Escape hooks; events via `tauriApi`
- Removed unused Tauri scaffold `App.tsx`; `index.html` title/icon → OctopPet
Expand Down
42 changes: 13 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,12 @@
<p align="center">
<img src="assets/octop-app-icon.png" alt="Octop Pet" width="128" />
</p>

<p align="center">
<strong>A desktop companion for your self-hosted Octop — floating mascot, compact chat, system tray.</strong>
</p>

<p align="center">
<a href="https://nodejs.org/"><img alt="Node.js LTS" src="https://img.shields.io/badge/node-LTS-green?logo=node.js&logoColor=white" /></a>
<a href="https://www.rust-lang.org/"><img alt="Rust stable" src="https://img.shields.io/badge/rust-stable-orange?logo=rust&logoColor=white" /></a>
<a href="https://tauri.app/"><img alt="Tauri 2" src="https://img.shields.io/badge/Tauri-2-blue?logo=tauri&logoColor=white" /></a>
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green" /></a>
</p>

<p align="center">
<a href="#-highlights">Highlights</a> ·
<a href="#-overview">Overview</a> ·
<a href="#-quick-start">Quick Start</a> ·
<a href="#-usage">Usage</a> ·
<a href="#-development">Development</a> ·
<a href="#-contents">Contents</a>
</p>

<p align="center">
<b>English</b> · <a href="README_CN.md">中文</a>
</p>
![Octop Pet](assets/octop-app-icon.png)

**A desktop companion for your self-hosted Octop — floating mascot, compact chat, system tray.**

![Node.js LTS](https://img.shields.io/badge/node-LTS-green?logo=node.js&logoColor=white)![Rust stable](https://img.shields.io/badge/rust-stable-orange?logo=rust&logoColor=white)![Tauri 2](https://img.shields.io/badge/Tauri-2-blue?logo=tauri&logoColor=white)![License: MIT](https://img.shields.io/badge/license-MIT-green)

[Highlights](#-highlights) · [Overview](#-overview) · [Quick Start](#-quick-start) · [Usage](#-usage) · [Development](#-development) · [Contents](#-contents)

**English** · [中文](README_CN.md)

---

Expand Down Expand Up @@ -96,7 +78,7 @@ In a separate terminal, start your Octop server:
octop run --host 127.0.0.1 --port 8088
```

Open **http://127.0.0.1:8088** and create at least one agent if you have none yet.
Open **[http://127.0.0.1:8088](http://127.0.0.1:8088)** and create at least one agent if you have none yet.

### 3. Connect Octop Pet

Expand All @@ -107,6 +89,8 @@ In **Settings**:
3. Click **测试连接** (Test connection) to verify credentials
4. Click **保存** (Save)

Optional: in the **窗口** tab, uncheck **点击其他应用时保持窗口显示** to hide chat and settings when clicking the desktop or another app. The pet always stays visible.

The service URL and username are stored in app config; passwords and access tokens use the OS keyring.

### 4. Chat
Expand Down Expand Up @@ -232,7 +216,7 @@ src-tauri/src/

## 📦 Release

Push a version tag to trigger [`.github/workflows/release.yml`](.github/workflows/release.yml). Keep versions in sync:
Push a version tag to trigger `[.github/workflows/release.yml](.github/workflows/release.yml)`. Keep versions in sync:

```sh
make sync-version VERSION=0.2.0
Expand Down
4 changes: 3 additions & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ octop run --host 127.0.0.1 --port 8088
3. 点击 **测试连接** 验证凭据
4. 点击 **保存**

可选:在 **窗口** 中取消「点击其他应用时保持窗口显示」,点到桌面或其他应用时会隐藏聊天和设置;桌宠始终显示。

服务地址与用户名保存在应用配置中;密码与访问令牌通过系统钥匙串存储。

### 4. 开始聊天
Expand Down Expand Up @@ -128,7 +130,7 @@ octop run --host 127.0.0.1 --port 8088

### 聊天窗口

- **关闭圆点** 仅隐藏窗口,应用继续在托盘运行
- **关闭按钮** 仅隐藏窗口,应用继续在托盘运行(macOS 为左上红点,Windows / Linux 为右上 ×)
- **新建会话** 为当前 Agent 创建新线程
- **切换 Agent** 恢复该 Agent 已映射的线程(若远程仍有效)

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Octop Pet is a **desktop client** for a remote Octop server. Security responsibi

### Octop Pet (this repo)

- Passwords and access tokens are stored in the **OS keyring** (macOS Keychain / Windows Credential Manager), not in plain-text config files
- Passwords and access tokens are stored in the **OS keyring** (macOS Keychain / Windows Credential Manager), not in `config.json`. Debug/`tauri dev` builds use a 0600 file under the app data directory instead, so unsigned rebuilds do not trigger Keychain ACL prompts
- `config.json` holds non-secret settings only (`baseUrl`, username, mascot, thread map, window position, shortcuts)
- HTTP/WebSocket calls go to the user-configured Octop base URL — the app does not phone home to third parties
- Never commit `.env` files, signing certificates (`.p12`, `.pfx`), or captured tokens in issues or PRs
Expand Down
17 changes: 17 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,11 @@ tauri-plugin-http = "2"

[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.6"
objc2-app-kit = { version = "0.3", features = ["NSColor", "NSWindow"] }
objc2-app-kit = { version = "0.3", features = [
"NSAnimation",
"NSAnimationContext",
"NSColor",
"NSWindow",
"objc2-quartz-core",
] }
objc2-quartz-core = { version = "0.3", features = ["CAMediaTimingFunction"] }
3 changes: 3 additions & 0 deletions src-tauri/src/config_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct AppConfig {
pub pet_y: Option<f64>,
pub shortcut_open_pet: String,
pub shortcut_open_home: String,
pub keep_windows_visible: bool,
}

impl Default for AppConfig {
Expand All @@ -34,6 +35,7 @@ impl Default for AppConfig {
pet_y: None,
shortcut_open_pet: "CmdOrCtrl+Shift+O".into(),
shortcut_open_home: "CmdOrCtrl+Shift+H".into(),
keep_windows_visible: true,
}
}
}
Expand Down Expand Up @@ -97,6 +99,7 @@ fn merge_patch(cfg: &mut AppConfig, patch: Value) -> Result<(), String> {
"petY" => cfg.pet_y = patch_field(key, value.clone())?,
"shortcutOpenPet" => cfg.shortcut_open_pet = patch_field(key, value.clone())?,
"shortcutOpenHome" => cfg.shortcut_open_home = patch_field(key, value.clone())?,
"keepWindowsVisible" => cfg.keep_windows_visible = patch_field(key, value.clone())?,
_ => return Err(format!("unsupported config field: {key}")),
}
}
Expand Down
13 changes: 7 additions & 6 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ pub fn run() {
}
}
}
tauri::WindowEvent::Focused(_)
| tauri::WindowEvent::ThemeChanged(_)
| tauri::WindowEvent::ScaleFactorChanged { .. }
tauri::WindowEvent::Focused(focused) => {
window_cmd::handle_window_focus_change(window, *focused);
}
tauri::WindowEvent::ThemeChanged(_) | tauri::WindowEvent::ScaleFactorChanged { .. }
if window.label() == "pet" =>
{
window_cmd::ensure_pet_transparent(window.app_handle());
Expand All @@ -39,10 +40,8 @@ pub fn run() {
}
tray::setup(app)?;
window_cmd::ensure_pet_transparent(app.handle());
window_cmd::apply_window_deactivate_policy(app.handle().clone())?;
window_cmd::spawn_pet_transparency_watchdog(app.handle());
if let Some(chat) = app.get_webview_window("chat") {
let _ = chat.set_resizable(false);
}
Ok(())
})
.invoke_handler(tauri::generate_handler![
Expand All @@ -59,6 +58,8 @@ pub fn run() {
window_cmd::show_settings,
window_cmd::place_window_bottom_center,
window_cmd::place_window_centered,
window_cmd::apply_bottom_anchored_size,
window_cmd::apply_window_deactivate_policy,
tray::reload_hotkeys,
])
.run(tauri::generate_context!())
Expand Down
Loading
Loading