thepopebot 1.2.41 → 1.2.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/chat/actions.js +2 -5
- package/lib/chat/components/sidebar-history-item.js +23 -3
- package/lib/chat/components/sidebar-history-item.jsx +21 -3
- package/lib/chat/components/ui/rename-dialog.js +74 -0
- package/lib/chat/components/ui/rename-dialog.jsx +72 -0
- package/lib/cron.js +17 -11
- package/package.json +1 -1
package/lib/chat/actions.js
CHANGED
|
@@ -164,11 +164,8 @@ export async function markNotificationsRead() {
|
|
|
164
164
|
*/
|
|
165
165
|
export async function getAppVersion() {
|
|
166
166
|
await requireAuth();
|
|
167
|
-
const {
|
|
168
|
-
|
|
169
|
-
const version = require('../../../package.json').version;
|
|
170
|
-
const { getUpdateAvailable } = await import('../cron.js');
|
|
171
|
-
return { version, updateAvailable: getUpdateAvailable() };
|
|
167
|
+
const { getInstalledVersion, getUpdateAvailable } = await import('../cron.js');
|
|
168
|
+
return { version: getInstalledVersion(), updateAvailable: getUpdateAvailable() };
|
|
172
169
|
}
|
|
173
170
|
|
|
174
171
|
/**
|
|
@@ -5,6 +5,7 @@ import { MessageIcon, TrashIcon, MoreHorizontalIcon, StarIcon, StarFilledIcon, P
|
|
|
5
5
|
import { SidebarMenuButton, SidebarMenuItem, useSidebar } from "./ui/sidebar.js";
|
|
6
6
|
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from "./ui/dropdown-menu.js";
|
|
7
7
|
import { ConfirmDialog } from "./ui/confirm-dialog.js";
|
|
8
|
+
import { RenameDialog } from "./ui/rename-dialog.js";
|
|
8
9
|
import { useChatNav } from "./chat-nav-context.js";
|
|
9
10
|
import { cn } from "../utils.js";
|
|
10
11
|
function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename }) {
|
|
@@ -13,9 +14,11 @@ function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename }) {
|
|
|
13
14
|
const [hovered, setHovered] = useState(false);
|
|
14
15
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
15
16
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
17
|
+
const [renameDialogOpen, setRenameDialogOpen] = useState(false);
|
|
16
18
|
const [editing, setEditing] = useState(false);
|
|
17
19
|
const [editTitle, setEditTitle] = useState(chat.title || "");
|
|
18
20
|
const inputRef = useRef(null);
|
|
21
|
+
const clickTimer = useRef(null);
|
|
19
22
|
const showMenu = hovered || dropdownOpen;
|
|
20
23
|
useEffect(() => {
|
|
21
24
|
if (editing && inputRef.current) {
|
|
@@ -23,6 +26,11 @@ function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename }) {
|
|
|
23
26
|
inputRef.current.select();
|
|
24
27
|
}
|
|
25
28
|
}, [editing]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
return () => {
|
|
31
|
+
if (clickTimer.current) clearTimeout(clickTimer.current);
|
|
32
|
+
};
|
|
33
|
+
}, []);
|
|
26
34
|
const startRename = () => {
|
|
27
35
|
setEditTitle(chat.title || "");
|
|
28
36
|
setEditing(true);
|
|
@@ -69,8 +77,10 @@ function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename }) {
|
|
|
69
77
|
className: "pr-8",
|
|
70
78
|
isActive,
|
|
71
79
|
onClick: () => {
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
clickTimer.current = setTimeout(() => {
|
|
81
|
+
navigateToChat(chat.id);
|
|
82
|
+
setOpenMobile(false);
|
|
83
|
+
}, 250);
|
|
74
84
|
},
|
|
75
85
|
children: [
|
|
76
86
|
/* @__PURE__ */ jsx(MessageIcon, { size: 14 }),
|
|
@@ -81,6 +91,7 @@ function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename }) {
|
|
|
81
91
|
onDoubleClick: (e) => {
|
|
82
92
|
e.stopPropagation();
|
|
83
93
|
e.preventDefault();
|
|
94
|
+
if (clickTimer.current) clearTimeout(clickTimer.current);
|
|
84
95
|
startRename();
|
|
85
96
|
},
|
|
86
97
|
children: chat.title
|
|
@@ -124,7 +135,7 @@ function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename }) {
|
|
|
124
135
|
{
|
|
125
136
|
onClick: (e) => {
|
|
126
137
|
e.stopPropagation();
|
|
127
|
-
|
|
138
|
+
setRenameDialogOpen(true);
|
|
128
139
|
},
|
|
129
140
|
children: [
|
|
130
141
|
/* @__PURE__ */ jsx(PencilIcon, { size: 14 }),
|
|
@@ -165,6 +176,15 @@ function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename }) {
|
|
|
165
176
|
},
|
|
166
177
|
onCancel: () => setConfirmDelete(false)
|
|
167
178
|
}
|
|
179
|
+
),
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
RenameDialog,
|
|
182
|
+
{
|
|
183
|
+
open: renameDialogOpen,
|
|
184
|
+
currentValue: chat.title || "",
|
|
185
|
+
onSave: (newTitle) => onRename(chat.id, newTitle),
|
|
186
|
+
onCancel: () => setRenameDialogOpen(false)
|
|
187
|
+
}
|
|
168
188
|
)
|
|
169
189
|
] });
|
|
170
190
|
}
|
|
@@ -5,6 +5,7 @@ import { MessageIcon, TrashIcon, MoreHorizontalIcon, StarIcon, StarFilledIcon, P
|
|
|
5
5
|
import { SidebarMenuButton, SidebarMenuItem, useSidebar } from './ui/sidebar.js';
|
|
6
6
|
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from './ui/dropdown-menu.js';
|
|
7
7
|
import { ConfirmDialog } from './ui/confirm-dialog.js';
|
|
8
|
+
import { RenameDialog } from './ui/rename-dialog.js';
|
|
8
9
|
import { useChatNav } from './chat-nav-context.js';
|
|
9
10
|
import { cn } from '../utils.js';
|
|
10
11
|
|
|
@@ -14,9 +15,11 @@ export function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename
|
|
|
14
15
|
const [hovered, setHovered] = useState(false);
|
|
15
16
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
16
17
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
18
|
+
const [renameDialogOpen, setRenameDialogOpen] = useState(false);
|
|
17
19
|
const [editing, setEditing] = useState(false);
|
|
18
20
|
const [editTitle, setEditTitle] = useState(chat.title || '');
|
|
19
21
|
const inputRef = useRef(null);
|
|
22
|
+
const clickTimer = useRef(null);
|
|
20
23
|
|
|
21
24
|
const showMenu = hovered || dropdownOpen;
|
|
22
25
|
|
|
@@ -27,6 +30,12 @@ export function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename
|
|
|
27
30
|
}
|
|
28
31
|
}, [editing]);
|
|
29
32
|
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
return () => {
|
|
35
|
+
if (clickTimer.current) clearTimeout(clickTimer.current);
|
|
36
|
+
};
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
30
39
|
const startRename = () => {
|
|
31
40
|
setEditTitle(chat.title || '');
|
|
32
41
|
setEditing(true);
|
|
@@ -73,8 +82,10 @@ export function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename
|
|
|
73
82
|
className="pr-8"
|
|
74
83
|
isActive={isActive}
|
|
75
84
|
onClick={() => {
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
clickTimer.current = setTimeout(() => {
|
|
86
|
+
navigateToChat(chat.id);
|
|
87
|
+
setOpenMobile(false);
|
|
88
|
+
}, 250);
|
|
78
89
|
}}
|
|
79
90
|
>
|
|
80
91
|
<MessageIcon size={14} />
|
|
@@ -83,6 +94,7 @@ export function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename
|
|
|
83
94
|
onDoubleClick={(e) => {
|
|
84
95
|
e.stopPropagation();
|
|
85
96
|
e.preventDefault();
|
|
97
|
+
if (clickTimer.current) clearTimeout(clickTimer.current);
|
|
86
98
|
startRename();
|
|
87
99
|
}}
|
|
88
100
|
>
|
|
@@ -122,7 +134,7 @@ export function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename
|
|
|
122
134
|
<DropdownMenuItem
|
|
123
135
|
onClick={(e) => {
|
|
124
136
|
e.stopPropagation();
|
|
125
|
-
|
|
137
|
+
setRenameDialogOpen(true);
|
|
126
138
|
}}
|
|
127
139
|
>
|
|
128
140
|
<PencilIcon size={14} />
|
|
@@ -155,6 +167,12 @@ export function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename
|
|
|
155
167
|
}}
|
|
156
168
|
onCancel={() => setConfirmDelete(false)}
|
|
157
169
|
/>
|
|
170
|
+
<RenameDialog
|
|
171
|
+
open={renameDialogOpen}
|
|
172
|
+
currentValue={chat.title || ''}
|
|
173
|
+
onSave={(newTitle) => onRename(chat.id, newTitle)}
|
|
174
|
+
onCancel={() => setRenameDialogOpen(false)}
|
|
175
|
+
/>
|
|
158
176
|
</SidebarMenuItem>
|
|
159
177
|
);
|
|
160
178
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useRef } from "react";
|
|
4
|
+
function RenameDialog({ open, onSave, onCancel, title = "Rename chat", currentValue = "" }) {
|
|
5
|
+
const [value, setValue] = useState(currentValue);
|
|
6
|
+
const inputRef = useRef(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (open) {
|
|
9
|
+
setValue(currentValue);
|
|
10
|
+
setTimeout(() => {
|
|
11
|
+
if (inputRef.current) {
|
|
12
|
+
inputRef.current.focus();
|
|
13
|
+
inputRef.current.select();
|
|
14
|
+
}
|
|
15
|
+
}, 0);
|
|
16
|
+
}
|
|
17
|
+
}, [open, currentValue]);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!open) return;
|
|
20
|
+
const handleEsc = (e) => {
|
|
21
|
+
if (e.key === "Escape") onCancel();
|
|
22
|
+
};
|
|
23
|
+
document.addEventListener("keydown", handleEsc);
|
|
24
|
+
return () => document.removeEventListener("keydown", handleEsc);
|
|
25
|
+
}, [open, onCancel]);
|
|
26
|
+
const handleSave = () => {
|
|
27
|
+
const trimmed = value.trim();
|
|
28
|
+
if (trimmed && trimmed !== currentValue) {
|
|
29
|
+
onSave(trimmed);
|
|
30
|
+
}
|
|
31
|
+
onCancel();
|
|
32
|
+
};
|
|
33
|
+
if (!open) return null;
|
|
34
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
35
|
+
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/50", onClick: onCancel }),
|
|
36
|
+
/* @__PURE__ */ jsxs("div", { className: "relative z-50 w-full max-w-sm rounded-lg border border-border bg-background p-6 shadow-lg", onClick: (e) => e.stopPropagation(), children: [
|
|
37
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: title }),
|
|
38
|
+
/* @__PURE__ */ jsx(
|
|
39
|
+
"input",
|
|
40
|
+
{
|
|
41
|
+
ref: inputRef,
|
|
42
|
+
type: "text",
|
|
43
|
+
value,
|
|
44
|
+
onChange: (e) => setValue(e.target.value),
|
|
45
|
+
onKeyDown: (e) => {
|
|
46
|
+
if (e.key === "Enter") handleSave();
|
|
47
|
+
},
|
|
48
|
+
className: "mt-3 w-full rounded-md border border-input bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [
|
|
52
|
+
/* @__PURE__ */ jsx(
|
|
53
|
+
"button",
|
|
54
|
+
{
|
|
55
|
+
onClick: onCancel,
|
|
56
|
+
className: "rounded-md px-3 py-1.5 text-sm font-medium border border-input bg-background hover:bg-muted",
|
|
57
|
+
children: "Cancel"
|
|
58
|
+
}
|
|
59
|
+
),
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
"button",
|
|
62
|
+
{
|
|
63
|
+
onClick: handleSave,
|
|
64
|
+
className: "rounded-md px-3 py-1.5 text-sm font-medium text-white bg-foreground hover:bg-foreground/90",
|
|
65
|
+
children: "Save"
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
] })
|
|
69
|
+
] })
|
|
70
|
+
] });
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
RenameDialog
|
|
74
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useRef } from 'react';
|
|
4
|
+
|
|
5
|
+
export function RenameDialog({ open, onSave, onCancel, title = 'Rename chat', currentValue = '' }) {
|
|
6
|
+
const [value, setValue] = useState(currentValue);
|
|
7
|
+
const inputRef = useRef(null);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (open) {
|
|
11
|
+
setValue(currentValue);
|
|
12
|
+
setTimeout(() => {
|
|
13
|
+
if (inputRef.current) {
|
|
14
|
+
inputRef.current.focus();
|
|
15
|
+
inputRef.current.select();
|
|
16
|
+
}
|
|
17
|
+
}, 0);
|
|
18
|
+
}
|
|
19
|
+
}, [open, currentValue]);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!open) return;
|
|
23
|
+
const handleEsc = (e) => {
|
|
24
|
+
if (e.key === 'Escape') onCancel();
|
|
25
|
+
};
|
|
26
|
+
document.addEventListener('keydown', handleEsc);
|
|
27
|
+
return () => document.removeEventListener('keydown', handleEsc);
|
|
28
|
+
}, [open, onCancel]);
|
|
29
|
+
|
|
30
|
+
const handleSave = () => {
|
|
31
|
+
const trimmed = value.trim();
|
|
32
|
+
if (trimmed && trimmed !== currentValue) {
|
|
33
|
+
onSave(trimmed);
|
|
34
|
+
}
|
|
35
|
+
onCancel();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (!open) return null;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
42
|
+
<div className="fixed inset-0 bg-black/50" onClick={onCancel} />
|
|
43
|
+
<div className="relative z-50 w-full max-w-sm rounded-lg border border-border bg-background p-6 shadow-lg" onClick={(e) => e.stopPropagation()}>
|
|
44
|
+
<h3 className="text-lg font-semibold">{title}</h3>
|
|
45
|
+
<input
|
|
46
|
+
ref={inputRef}
|
|
47
|
+
type="text"
|
|
48
|
+
value={value}
|
|
49
|
+
onChange={(e) => setValue(e.target.value)}
|
|
50
|
+
onKeyDown={(e) => {
|
|
51
|
+
if (e.key === 'Enter') handleSave();
|
|
52
|
+
}}
|
|
53
|
+
className="mt-3 w-full rounded-md border border-input bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
54
|
+
/>
|
|
55
|
+
<div className="mt-4 flex justify-end gap-2">
|
|
56
|
+
<button
|
|
57
|
+
onClick={onCancel}
|
|
58
|
+
className="rounded-md px-3 py-1.5 text-sm font-medium border border-input bg-background hover:bg-muted"
|
|
59
|
+
>
|
|
60
|
+
Cancel
|
|
61
|
+
</button>
|
|
62
|
+
<button
|
|
63
|
+
onClick={handleSave}
|
|
64
|
+
className="rounded-md px-3 py-1.5 text-sm font-medium text-white bg-foreground hover:bg-foreground/90"
|
|
65
|
+
>
|
|
66
|
+
Save
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
package/lib/cron.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import cron from 'node-cron';
|
|
2
2
|
import fs from 'fs';
|
|
3
|
-
import
|
|
4
|
-
import { dirname, join } from 'path';
|
|
3
|
+
import path from 'path';
|
|
5
4
|
import { cronsFile, cronDir } from './paths.js';
|
|
6
5
|
import { executeAction } from './actions.js';
|
|
7
6
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
)
|
|
7
|
+
// Installed version — resolved lazily on first version check.
|
|
8
|
+
// Can't read at module scope because webpack bakes in the build-time path
|
|
9
|
+
// (e.g. /Users/dev/project/...) which doesn't exist at runtime in Docker.
|
|
10
|
+
let _installedVersion = null;
|
|
11
|
+
|
|
12
|
+
function getInstalledVersion() {
|
|
13
|
+
if (!_installedVersion) {
|
|
14
|
+
const pkgPath = path.join(process.cwd(), 'node_modules', 'thepopebot', 'package.json');
|
|
15
|
+
_installedVersion = JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version;
|
|
16
|
+
}
|
|
17
|
+
return _installedVersion;
|
|
18
|
+
}
|
|
14
19
|
|
|
15
20
|
// In-memory flag for available update (read by sidebar, written by cron)
|
|
16
21
|
let _updateAvailable = null;
|
|
@@ -62,8 +67,9 @@ async function runVersionCheck() {
|
|
|
62
67
|
const data = await res.json();
|
|
63
68
|
const latest = data.version;
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
const installed = getInstalledVersion();
|
|
71
|
+
if (isVersionNewer(latest, installed)) {
|
|
72
|
+
console.log(`[version check] update available: ${installed} → ${latest}`);
|
|
67
73
|
setUpdateAvailable(latest);
|
|
68
74
|
// Persist to DB
|
|
69
75
|
const { setAvailableVersion } = await import('./db/update-check.js');
|
|
@@ -143,4 +149,4 @@ function loadCrons() {
|
|
|
143
149
|
return tasks;
|
|
144
150
|
}
|
|
145
151
|
|
|
146
|
-
export { loadCrons, startBuiltinCrons, getUpdateAvailable, setUpdateAvailable };
|
|
152
|
+
export { loadCrons, startBuiltinCrons, getUpdateAvailable, setUpdateAvailable, getInstalledVersion };
|