add update service

This commit is contained in:
2026-04-01 20:29:47 -07:00
parent d0863b675e
commit 58adf490cc
4 changed files with 59 additions and 18 deletions

View File

@@ -8,7 +8,7 @@
version="1.1"
id="svg1"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
sodipodi:docname="argonode.svg"
sodipodi:docname="favicon.svg"
inkscape:export-filename="tono.png"
inkscape:export-xdpi="130.05"
inkscape:export-ydpi="130.05"
@@ -49,28 +49,17 @@
height="194.43169"
x="2.5203834"
y="2.9819231"
rx="52.916668"
ry="52.916668" />
rx="97.484589"
ry="97.215843" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:8.251;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 80.438549,119.47945 85.556771,-87.012984 4.46561,16.665896 -16.7032,-4.47561"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:9.87521;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 62.915486,149.85992 165.31436,45.718188 l 5.34467,19.946625 -19.99127,-5.356646"
id="path6"
sodipodi:nodetypes="cccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:3.95096;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 133.7656,68.457841 H 103.74962 L 50.836303,122.45621 80.512342,121.94367"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:4.72872;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 126.74012,88.794566 H 90.815406 l -63.329451,64.628104 35.51785,-0.61344"
id="path7"
sodipodi:nodetypes="cccc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:80px;line-height:0;font-family:Futura;-inkscape-font-specification:'Futura, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;writing-mode:lr-tb;direction:ltr;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.75827;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
x="18.009901"
y="172.8485"
id="text8"><tspan
sodipodi:role="line"
id="tspan8"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:80px;font-family:Futura;-inkscape-font-specification:'Futura, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:2.75827;stroke-dasharray:none"
x="18.009901"
y="172.8485">tono</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -897,6 +897,7 @@ function Flow() {
const [executingNodeId, setExecutingNodeId] = useState<string | null>(null);
const [helpTabs, setHelpTabs] = useState<{ label: string; type?: string; content: string | null }[]>([]);
const [activeHelpTab, setActiveHelpTab] = useState<string | null>(null);
const [updateInfo, setUpdateInfo] = useState<{ latest: string; url: string } | null>(null);
const flowContainerRef = useRef<HTMLDivElement | null>(null);
const panTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -918,6 +919,19 @@ function Flow() {
const reactFlow = useReactFlow<TonoNode, TonoEdge>() as ReturnType<typeof useReactFlow<TonoNode, TonoEdge>> & { updateNodeInternals: (id: string) => void };
const undoRedo = useUndoRedo();
// ── Update check (native builds only) ──────────────────────────────
useEffect(() => {
if (!(window as any).pywebview) return;
fetch('/check-update')
.then((r) => r.ok ? r.json() : null)
.then((data) => {
if (data?.update_available && data.latest) {
setUpdateInfo({ latest: data.latest, url: data.url });
}
})
.catch(() => {});
}, []);
const scheduleAutoRun = useCallback(() => {
if (autoRunTimer.current) clearTimeout(autoRunTimer.current);
autoRunTimer.current = setTimeout(() => autoRunRef.current?.(), 300);
@@ -3179,6 +3193,15 @@ function Flow() {
<div className={`status-bar ${status.level}`}>{status.text}</div>
</div>
{updateInfo && (
<div className="update-banner">
tono {updateInfo.latest} is available.
{' '}
<a href={updateInfo.url} target="_blank" rel="noopener noreferrer">Download</a>
<button className="update-banner-dismiss" onClick={() => setUpdateInfo(null)} title="Dismiss"></button>
</div>
)}
{/* React Flow canvas */}
<div
ref={flowContainerRef}

View File

@@ -212,6 +212,34 @@ html, body, #root {
.status-bar.info { color: var(--accent-light); }
.status-bar.error { color: var(--error-text); background: var(--error-bg); }
.update-banner {
background: var(--accent);
color: #fff;
text-align: center;
padding: 4px 12px;
font-size: 12px;
position: relative;
}
.update-banner a {
color: #fff;
font-weight: 600;
text-decoration: underline;
}
.update-banner-dismiss {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: #fff;
cursor: pointer;
font-size: 13px;
padding: 2px 4px;
opacity: 0.7;
}
.update-banner-dismiss:hover { opacity: 1; }
/* ── React Flow container ──────────────────────────────────────────── */
.flow-container {
flex: 1;

View File

@@ -22,6 +22,7 @@ export default defineConfig({
'/file-content': 'http://127.0.0.1:8188',
'/help-docs': { target: 'http://127.0.0.1:8188', changeOrigin: true },
'/prompt': 'http://127.0.0.1:8188',
'/check-update': 'http://127.0.0.1:8188',
'/ws': {
target: 'http://127.0.0.1:8188',
ws: true,