Official
settings
Built-in settings UI (JavaScript UI plugin).
The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/settings@0.2.0
Permissions in 0.2.0
Files
views/updates.ts3.4 KB
// The Updates section: the app's own update, the default plugins' build,
// and when they were last checked. Install restarts when it must.
// HTM whitespace is significant; keep these templates out of Prettier's HTML formatter.
import { html as htm } from "convergence";
import type { EffectViewContext } from "convergence/effect";
import type { SettingsPage } from "../controller.ts";
import { card, notes, section, setting, column } from "./layout.ts";
function when(iso: string | null) {
if (!iso) return "never";
const minutes = Math.round((Date.now() - Date.parse(iso)) / 60000);
if (minutes < 1) return "just now";
if (minutes < 60) return `${minutes} min ago`;
const hours = Math.round(minutes / 60);
return hours < 24 ? `${hours} h ago` : new Date(iso).toLocaleDateString();
}
export function renderUpdates(ctx: EffectViewContext<object>, actions: SettingsPage["Service"]) {
const u = actions.state.updates;
if (!u) return htm`<empty>Loading…</empty>`;
if (!u.enabled) {
return section(
"Updates",
null,
card([
setting({
title: `Divergence ${u.appVersion}`,
description:
"This copy runs its plugins from a source tree, so it updates from the repository, not from releases.",
}),
]),
);
}
const busy = !!u.busy;
const app = u.appUpdate;
const plugins = u.pluginUpdate;
const current = u.plugins
? `Build ${u.plugins.build}, from Divergence ${u.plugins.version}.`
: "Not from a release.";
const downloading = u.busy === "downloading the update";
return htm`
<div ...${column} gap="large" padding="small">
${section(
"Updates",
u.error ? `Last check failed: ${u.error}` : `Checked ${when(u.lastChecked)}.`,
card([
setting({
title: app ? `Divergence ${app.version} is available` : `Divergence ${u.appVersion}`,
description: app
? `You have ${u.appVersion}. The app restarts to finish the update.`
: "This is the newest version.",
control: app
? htm`<button id="install-app" label=${downloading ? "Downloading…" : "Install and restart"} icon="circle-arrow-up" small variant="primary"
loading=${downloading} disabled=${busy} onClick=${() => ctx.run(actions.installApp())} />`
: htm`<button id="check-updates" label="Check now" icon="rotate-cw" small variant="outline"
loading=${u.checking} disabled=${u.checking || busy} onClick=${() => ctx.run(actions.checkNow())} />`,
}),
app ? notes(app.releaseNotes) : null,
setting({
title: plugins ? `New default plugins (build ${plugins.build})` : "Default plugins",
description: plugins
? "Plugins you have not changed update at once. For the ones you changed, an agent opens in the Plugins workspace and merges the update into your version."
: `${current} Up to date.`,
control: plugins
? htm`<button id="update-plugins" label="Update plugins" icon="circle-arrow-up" small variant=${app ? "outline" : "primary"}
loading=${u.busy === "updating the plugins"} disabled=${busy} onClick=${() => ctx.run(actions.applyPlugins())} />`
: null,
}),
plugins ? notes(plugins.releaseNotes) : null,
]),
)}
</div>`;
}Versions
| Version | Published | Plugin API | Size | Permissions | Status |
|---|---|---|---|---|---|
| 0.2.0latest | Oct 5, 2026 | >=2 <3 | 24.7 KB | 7 permissions | Listed |
No comments yet.