Skip to content

Commit 1051d74

Browse files
committed
feat(desktop-pet): add close button and empty token mode support
1 parent 59e4e2f commit 1051d74

6 files changed

Lines changed: 73 additions & 3 deletions

File tree

desktop/src-tauri/capabilities/default.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"permissions": [
77
"core:default",
88
"core:event:default",
9+
"core:window:allow-close",
910
"core:window:allow-start-dragging",
1011
"opener:default",
1112
"notification:default",

desktop/src/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<body>
1010
<div id="pet-container">
1111
<div id="pet-hint" class="pet-hint" hidden></div>
12+
<button id="close-btn" aria-label="关闭" tabindex="-1"></button>
1213
<canvas id="cat-canvas" width="64" height="64"></canvas>
1314
<div id="pet-label"></div>
1415
</div>

desktop/src/ipc.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,26 @@ function setPetLabel(text) {
4545
}
4646
}
4747

48+
// --- Close button ---
49+
50+
const closeBtn = document.getElementById("close-btn");
51+
52+
closeBtn.addEventListener("pointerdown", (event) => {
53+
event.preventDefault();
54+
event.stopPropagation(); // prevent drag initiation
55+
});
56+
57+
closeBtn.addEventListener("pointerup", (event) => {
58+
event.preventDefault();
59+
event.stopPropagation();
60+
if (window.__TAURI__) {
61+
const win = window.__TAURI__.window.getCurrentWindow();
62+
void win.close();
63+
} else {
64+
console.log("[IPC] Close button clicked (demo mode)");
65+
}
66+
});
67+
4868
// --- Click handler for pet container ---
4969

5070
const petContainer = document.getElementById("pet-container");

desktop/src/style.css

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,39 @@ body {
7777
transform: rotate(45deg);
7878
}
7979

80+
/* --- Close button (hover overlay) --- */
81+
82+
#close-btn {
83+
position: absolute;
84+
top: 2px;
85+
right: 2px;
86+
width: 18px;
87+
height: 18px;
88+
padding: 0;
89+
border: none;
90+
border-radius: 50%;
91+
background: rgba(0, 0, 0, 0.55);
92+
color: #fff;
93+
font-size: 13px;
94+
line-height: 1;
95+
display: flex;
96+
align-items: center;
97+
justify-content: center;
98+
cursor: pointer;
99+
opacity: 0;
100+
pointer-events: auto;
101+
transition: opacity 120ms ease;
102+
z-index: 10;
103+
}
104+
105+
#pet-container:hover #close-btn {
106+
opacity: 1;
107+
}
108+
109+
#close-btn:hover {
110+
background: rgba(239, 68, 68, 0.85);
111+
}
112+
80113
#cat-canvas {
81114
width: 64px;
82115
height: 64px;
@@ -131,6 +164,15 @@ body {
131164
border-bottom-color: rgba(148, 163, 184, 0.24);
132165
}
133166

167+
#close-btn {
168+
background: rgba(255, 255, 255, 0.3);
169+
color: #fff;
170+
}
171+
172+
#close-btn:hover {
173+
background: rgba(239, 68, 68, 0.85);
174+
}
175+
134176
.panel {
135177
background: rgba(40, 40, 40, 0.92);
136178
border-color: rgba(255, 255, 255, 0.1);

src/cccc/ports/web/routes/groups.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
read_group_prompt_file,
2222
write_group_prompt_file,
2323
)
24+
from ....kernel.access_tokens import list_access_tokens
2425
from ....util.conv import coerce_bool
2526
from ....util.fs import atomic_write_text
2627
from ..schemas import (
@@ -692,6 +693,9 @@ async def group_settings_get(group_id: str) -> Dict[str, Any]:
692693
async def group_desktop_pet_launch_token(request: Request, group_id: str) -> Dict[str, Any]:
693694
token = _request_access_token(request)
694695
if not token:
696+
# Empty password mode: no tokens configured → allow with empty token
697+
if not list_access_tokens():
698+
return {"ok": True, "result": {"token": ""}}
695699
raise HTTPException(
696700
status_code=403,
697701
detail={"code": "permission_denied", "message": "authentication required", "details": {}},

web/src/components/ContextModal.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ export function ContextModal({
644644

645645
const resolveDesktopPetLaunchToken = useCallback(async (): Promise<string | null> => {
646646
const launchResp = await fetchDesktopPetLaunchToken(groupId);
647-
if (!launchResp.ok || !launchResp.result?.token) {
647+
if (!launchResp.ok) {
648648
const raw = launchResp.error?.message || "";
649649
const code = launchResp.error?.code || "";
650650
const combined = `${code} ${raw}`;
@@ -654,15 +654,17 @@ export function ContextModal({
654654
: raw || tr("context.desktopPetLaunchTokenFailed", "Failed to load access tokens.");
655655
throw new Error(msg);
656656
}
657-
return String(launchResp.result.token || "").trim() || null;
657+
// Empty string token is valid in empty-password mode
658+
const token = launchResp.result?.token;
659+
return typeof token === "string" ? token : null;
658660
}, [groupId, tr]);
659661

660662
const handlePrepareDesktopPetLaunch = useCallback(async () => {
661663
setDesktopPetLaunchBusy(true);
662664
setDesktopPetLaunchError("");
663665
try {
664666
const token = await resolveDesktopPetLaunchToken();
665-
if (!token) {
667+
if (token === null) {
666668
setDesktopPetLaunchError(tr("context.desktopPetLaunchNoToken", "No access token is available for this group."));
667669
return;
668670
}

0 commit comments

Comments
 (0)