@songsid/agend 2.3.0-beta.1 → 2.3.0-beta.11
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/dist/agent-cli-instructions.md +5 -2
- package/dist/agent-cli.js +43 -7
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +4 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-file-read.d.ts +14 -6
- package/dist/agent-file-read.js +207 -19
- package/dist/agent-file-read.js.map +1 -1
- package/dist/agent-whats-new.d.ts +11 -0
- package/dist/agent-whats-new.js +29 -15
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +30 -1
- package/dist/auth-api.js +45 -13
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/authority-state.d.ts +38 -0
- package/dist/authority/authority-state.js +114 -0
- package/dist/authority/authority-state.js.map +1 -0
- package/dist/authority/session-authorizations.d.ts +43 -0
- package/dist/authority/session-authorizations.js +102 -0
- package/dist/authority/session-authorizations.js.map +1 -0
- package/dist/authority/web-authority.d.ts +120 -0
- package/dist/authority/web-authority.js +157 -0
- package/dist/authority/web-authority.js.map +1 -0
- package/dist/backend/claude-code.d.ts +2 -2
- package/dist/backend/claude-code.js +10 -5
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/types.d.ts +14 -0
- package/dist/backend/types.js +25 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-availability.d.ts +120 -0
- package/dist/backend-availability.js +396 -0
- package/dist/backend-availability.js.map +1 -0
- package/dist/backend-command.d.ts +21 -3
- package/dist/backend-command.js +23 -4
- package/dist/backend-command.js.map +1 -1
- package/dist/bundled-skills.d.ts +14 -0
- package/dist/bundled-skills.js +29 -0
- package/dist/bundled-skills.js.map +1 -0
- package/dist/cache-analysis.d.ts +47 -1
- package/dist/cache-analysis.js +68 -8
- package/dist/cache-analysis.js.map +1 -1
- package/dist/cache-ledger.d.ts +16 -3
- package/dist/cache-ledger.js +33 -13
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-report-tool.d.ts +17 -0
- package/dist/cache-report-tool.js +40 -0
- package/dist/cache-report-tool.js.map +1 -0
- package/dist/cache-service.d.ts +70 -3
- package/dist/cache-service.js +178 -17
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +7 -0
- package/dist/channel/adapters/discord.js +35 -0
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +22 -10
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +6 -0
- package/dist/classic-channel-manager.d.ts +10 -5
- package/dist/classic-channel-manager.js +12 -9
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +130 -31
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +103 -4
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +7 -0
- package/dist/daemon.js +63 -16
- package/dist/daemon.js.map +1 -1
- package/dist/decision-list.d.ts +25 -0
- package/dist/decision-list.js +30 -0
- package/dist/decision-list.js.map +1 -0
- package/dist/delivery-outbox.d.ts +10 -0
- package/dist/delivery-outbox.js +12 -1
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-context.d.ts +10 -0
- package/dist/fleet-manager.d.ts +417 -11
- package/dist/fleet-manager.js +1387 -109
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-traffic.d.ts +74 -0
- package/dist/fleet-traffic.js +196 -0
- package/dist/fleet-traffic.js.map +1 -0
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +20 -5
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +3 -2
- package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +257 -23
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +478 -0
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
- package/dist/human-delivery-receipts.d.ts +24 -1
- package/dist/human-delivery-receipts.js +64 -11
- package/dist/human-delivery-receipts.js.map +1 -1
- package/dist/instance-config-impact.js +3 -1
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-names.d.ts +4 -0
- package/dist/instance-names.js +12 -0
- package/dist/instance-names.js.map +1 -0
- package/dist/instructions.d.ts +12 -1
- package/dist/instructions.js +13 -2
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +100 -8
- package/dist/locale.js.map +1 -1
- package/dist/ls-attach-resolve.d.ts +95 -0
- package/dist/ls-attach-resolve.js +174 -0
- package/dist/ls-attach-resolve.js.map +1 -0
- package/dist/memory-hogs.d.ts +27 -0
- package/dist/memory-hogs.js +124 -0
- package/dist/memory-hogs.js.map +1 -0
- package/dist/outbound-handlers.d.ts +10 -0
- package/dist/outbound-handlers.js +122 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +164 -4
- package/dist/outbound-schemas.js +99 -3
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +8 -2
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +9 -0
- package/dist/public-web-link.js +8 -0
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.d.ts +10 -1
- package/dist/quickstart-api.js +12 -4
- package/dist/quickstart-api.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +4 -0
- package/dist/reply-buttons-controller.js +2 -2
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/restart-progress.d.ts +65 -1
- package/dist/restart-progress.js +236 -10
- package/dist/restart-progress.js.map +1 -1
- package/dist/rollout-index.d.ts +5 -0
- package/dist/rollout-index.js +26 -5
- package/dist/rollout-index.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +12 -0
- package/dist/scheduler/scheduler.js +83 -10
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/service-installer.d.ts +2 -1
- package/dist/service-installer.js +18 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.d.ts +19 -0
- package/dist/settings-api.js +137 -2
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +104 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +17 -1
- package/dist/settings-effect.js.map +1 -1
- package/dist/tmux-manager.d.ts +5 -0
- package/dist/tmux-manager.js +16 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.d.ts +4 -6
- package/dist/tool-permissions.js +53 -5
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.d.ts +7 -1
- package/dist/topic-commands.js +18 -4
- package/dist/topic-commands.js.map +1 -1
- package/dist/types.d.ts +48 -1
- package/dist/ui/app-effects.js +77 -31
- package/dist/ui/chat-ambient.js +307 -0
- package/dist/ui/chat-effect-posters.js +8 -8
- package/dist/ui/chat-effects.js +101 -13
- package/dist/ui/chat-render.js +21 -3
- package/dist/ui/chat-strings.js +59 -6
- package/dist/ui/chat-thread.js +159 -21
- package/dist/ui/effect-trial-controller.js +13 -8
- package/dist/ui/effect-trial-panel.js +19 -16
- package/dist/ui/fleet-cache.js +23 -1
- package/dist/ui/fleet-canvas-layout.js +111 -0
- package/dist/ui/fleet-canvas.js +525 -0
- package/dist/ui/fleet-org.js +27 -12
- package/dist/ui/fleet-traffic.js +343 -0
- package/dist/ui/fleet-usage.js +401 -0
- package/dist/ui/panel-chat.js +154 -49
- package/dist/ui/panel-details.js +2 -1
- package/dist/ui/panel-fleet.js +43 -17
- package/dist/ui/panel-settings.js +26 -8
- package/dist/ui/preview.js +3 -2
- package/dist/ui/settings-appearance.js +42 -18
- package/dist/ui/settings-catalog.js +92 -0
- package/dist/ui/settings-dialogs.js +40 -12
- package/dist/ui/settings-effects.js +4 -3
- package/dist/ui/settings-guests.js +117 -0
- package/dist/ui/settings-learn.js +25 -0
- package/dist/ui/settings-model.js +15 -6
- package/dist/ui/settings-mods.js +23 -9
- package/dist/ui/settings-owner-effects.js +9 -9
- package/dist/ui/settings-strings.js +120 -42
- package/dist/ui/settings-web-authority.js +91 -0
- package/dist/ui/shared/agend-auth.js +1 -1
- package/dist/ui/shared/app-i18n.js +4 -2
- package/dist/ui/shared/app-prefs.js +5 -4
- package/dist/ui/shared/app-route.js +19 -1
- package/dist/ui/shared/app-session.js +2 -0
- package/dist/ui/shared/app-shell.js +92 -27
- package/dist/ui/shared/app-store.js +26 -1
- package/dist/ui/shared/app-stream.js +3 -1
- package/dist/ui/shared/app-themes.js +24 -6
- package/dist/ui/shared/app.css +365 -17
- package/dist/ui/shared/app.js +21 -14
- package/dist/ui/shared/card-strings.js +4 -0
- package/dist/ui/shared/effect-app-runtime.js +68 -19
- package/dist/ui/shared/effect-chat-runtime.js +16 -3
- package/dist/ui/shared/effect-frame.js +26 -7
- package/dist/ui/shared/effect-geometry.js +24 -9
- package/dist/ui/shared/effect-policy.js +21 -7
- package/dist/ui/shared/effect-profiler.js +31 -9
- package/dist/ui/shared/effect-registry.js +87 -48
- package/dist/ui/shared/effect-stock-snapshot.js +9 -0
- package/dist/ui/shared/effect-stock-trust.js +62 -10
- package/dist/ui/shared/effect-trial-shadow.js +62 -0
- package/dist/ui/shared/effect-trial-source.js +1 -1
- package/dist/ui/shared/effect-trial-stage.js +54 -31
- package/dist/ui/shared/effect-trial.css +40 -0
- package/dist/ui/shared/fx-motion.js +67 -0
- package/dist/ui/shared/identity-card.js +75 -4
- package/dist/ui/shared/identity-color.js +3 -3
- package/dist/ui/shared/instance-nav.js +194 -28
- package/dist/ui/shared/panel-view.js +24 -4
- package/dist/ui/shared/tokens.css +9 -3
- package/dist/ui/shared/ui-icons.js +1 -0
- package/dist/ui/shared/usage-dialog.js +22 -2
- package/dist/ui/shared/view-strings.js +68 -2
- package/dist/ui/shared/web-authorize.js +151 -0
- package/dist/ui/signin.html +1 -1
- package/dist/ui/web-terminal/terminal.css +4 -4
- package/dist/usage/providers.d.ts +28 -0
- package/dist/usage/providers.js +126 -36
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage-report.d.ts +158 -0
- package/dist/usage-report.js +226 -0
- package/dist/usage-report.js.map +1 -0
- package/dist/usage-rollup.d.ts +51 -0
- package/dist/usage-rollup.js +168 -0
- package/dist/usage-rollup.js.map +1 -0
- package/dist/view-api.d.ts +39 -0
- package/dist/view-api.js +65 -4
- package/dist/view-api.js.map +1 -1
- package/dist/web-ambient.d.ts +72 -0
- package/dist/web-ambient.js +161 -0
- package/dist/web-ambient.js.map +1 -0
- package/dist/web-api.d.ts +69 -5
- package/dist/web-api.js +362 -45
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-policy.d.ts +4 -6
- package/dist/web-appearance-policy.js +5 -12
- package/dist/web-appearance-policy.js.map +1 -1
- package/dist/web-appearance-tools.d.ts +3 -0
- package/dist/web-appearance-tools.js +22 -3
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance-view.d.ts +27 -2
- package/dist/web-appearance-view.js +129 -14
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-appearance.d.ts +17 -3
- package/dist/web-appearance.js +82 -7
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-asset-parts.d.ts +5 -10
- package/dist/web-asset-parts.js +3 -3
- package/dist/web-asset-parts.js.map +1 -1
- package/dist/web-auth.js +3 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-card.d.ts +14 -0
- package/dist/web-card.js +40 -6
- package/dist/web-card.js.map +1 -1
- package/dist/web-channel-echo.d.ts +7 -1
- package/dist/web-channel-echo.js +6 -2
- package/dist/web-channel-echo.js.map +1 -1
- package/dist/web-chat-history.d.ts +50 -0
- package/dist/web-chat-history.js +87 -5
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +1 -0
- package/dist/web-effect-bootstrap.d.ts +12 -0
- package/dist/web-effect-bootstrap.js +49 -0
- package/dist/web-effect-bootstrap.js.map +1 -0
- package/dist/web-effect-bundle.js +46 -1
- package/dist/web-effect-bundle.js.map +1 -1
- package/dist/web-effect-playback.d.ts +30 -0
- package/dist/web-effect-playback.js +71 -0
- package/dist/web-effect-playback.js.map +1 -0
- package/dist/web-effect-profiles.d.ts +4 -4
- package/dist/web-effect-service.d.ts +11 -4
- package/dist/web-effect-service.js +28 -5
- package/dist/web-effect-service.js.map +1 -1
- package/dist/web-effect-tools.d.ts +1 -0
- package/dist/web-effect-tools.js +8 -5
- package/dist/web-effect-tools.js.map +1 -1
- package/dist/web-effect-trials.d.ts +4 -4
- package/dist/web-effect-trials.js +6 -6
- package/dist/web-effect-trials.js.map +1 -1
- package/dist/web-effect-upload.d.ts +2 -4
- package/dist/web-effect-upload.js +3 -8
- package/dist/web-effect-upload.js.map +1 -1
- package/dist/web-effect-worker.d.ts +1 -1
- package/dist/web-effect-worker.js +2 -2
- package/dist/web-effect-worker.js.map +1 -1
- package/dist/web-effect.d.ts +14 -0
- package/dist/web-effect.js +21 -0
- package/dist/web-effect.js.map +1 -1
- package/dist/web-guest-access.d.ts +125 -0
- package/dist/web-guest-access.js +234 -0
- package/dist/web-guest-access.js.map +1 -0
- package/dist/web-guest-rooms.d.ts +82 -0
- package/dist/web-guest-rooms.js +135 -0
- package/dist/web-guest-rooms.js.map +1 -0
- package/dist/web-identity-color.d.ts +1 -1
- package/dist/web-identity-color.js +2 -2
- package/dist/web-identity-color.js.map +1 -1
- package/dist/web-identity-palette.d.ts +1 -1
- package/dist/web-identity-palette.js +2 -2
- package/dist/web-identity-palette.js.map +1 -1
- package/dist/web-image-meta.d.ts +13 -0
- package/dist/web-image-meta.js +62 -0
- package/dist/web-image-meta.js.map +1 -1
- package/dist/web-login.d.ts +47 -0
- package/dist/web-login.js +115 -0
- package/dist/web-login.js.map +1 -1
- package/dist/web-mod-agent.d.ts +1 -1
- package/dist/web-mod-agent.js +12 -8
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-mod-bundle.d.ts +14 -1
- package/dist/web-mod-bundle.js +68 -3
- package/dist/web-mod-bundle.js.map +1 -1
- package/dist/web-mod-export.d.ts +9 -0
- package/dist/web-mod-export.js +21 -0
- package/dist/web-mod-export.js.map +1 -1
- package/dist/web-mod-store.d.ts +7 -1
- package/dist/web-mod-store.js +21 -7
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-prefs.d.ts +14 -0
- package/dist/web-prefs.js +24 -4
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +6 -2
- package/dist/web-preview.js +4 -2
- package/dist/web-preview.js.map +1 -1
- package/dist/web-returning-device.d.ts +41 -0
- package/dist/web-returning-device.js +57 -5
- package/dist/web-returning-device.js.map +1 -1
- package/dist/web-session-authorization.d.ts +132 -0
- package/dist/web-session-authorization.js +193 -0
- package/dist/web-session-authorization.js.map +1 -0
- package/dist/web-session.d.ts +70 -10
- package/dist/web-session.js +137 -22
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +49 -2
- package/dist/web-settings-patch.js +145 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +89 -0
- package/dist/web-settings-tools.js +270 -0
- package/dist/web-settings-tools.js.map +1 -0
- package/dist/web-shell-routes.d.ts +1 -1
- package/dist/web-shell-routes.js +1 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-theme-store.d.ts +39 -0
- package/dist/web-theme-store.js +122 -11
- package/dist/web-theme-store.js.map +1 -1
- package/dist/web-theme.d.ts +2 -0
- package/dist/web-theme.js +10 -1
- package/dist/web-theme.js.map +1 -1
- package/dist/web-upload.d.ts +13 -0
- package/dist/web-upload.js +39 -3
- package/dist/web-upload.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/package.json +1 -1
- package/templates/systemd.service.ejs +8 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// #1920 phase 3 (P3b-2): Settings → Web → Guests — guest sign-in for people who are not admins. The switches (guest
|
|
2
|
+
// sign-in, guests may chat), the agents an open connection's guests may reach, and every guest account: who, on which
|
|
3
|
+
// platform, what it reaches now, its sessions and devices, with Sign out (at once) and Narrow (staged with the rest).
|
|
4
|
+
// Changes go through Review changes → Apply like every Settings edit: one that widens what guests reach waits for a
|
|
5
|
+
// fleet admin's confirmation (the server's settings-change.ts), one that narrows it applies at once.
|
|
6
|
+
import { html, useEffect, useMemo, useState } from "/assets/app-html.js";
|
|
7
|
+
import { t } from "/assets/app-i18n.js";
|
|
8
|
+
import { toast } from "/assets/ui-toast.js";
|
|
9
|
+
import { confirmDialog } from "/assets/ui-confirm.js";
|
|
10
|
+
import { api } from "./settings-confirm.js";
|
|
11
|
+
import "./settings-strings.js";
|
|
12
|
+
|
|
13
|
+
const tn = (k, ...v) => t(`settings.${k}`, ...v);
|
|
14
|
+
const PLATFORMS = { discord: "Discord", telegram: "Telegram" };
|
|
15
|
+
const own = (o, k) => !!o && Object.prototype.hasOwnProperty.call(o, k);
|
|
16
|
+
const copyLists = (m) => { const out = {}; for (const [k, v] of Object.entries(m || {})) if (Array.isArray(v)) out[k] = [...v]; return out; };
|
|
17
|
+
const sameList = (a, b) => (a || []).length === (b || []).length && (a || []).every((n) => (b || []).includes(n));
|
|
18
|
+
const accountKey = (g) => `${g.adapterId}:${g.userId}`;
|
|
19
|
+
|
|
20
|
+
/** What changed against the configuration, as the PUT /api/settings/fleet/web body's `guests` (null: nothing). */
|
|
21
|
+
export function guestsBody(original, f) {
|
|
22
|
+
const out = {};
|
|
23
|
+
if (f.enabled !== original.enabled) out.enabled = f.enabled ? true : null; // off is the default
|
|
24
|
+
if (f.chat !== original.chat) out.chat = f.chat ? null : false; // on is the default
|
|
25
|
+
for (const key of ["open_instances", "narrow"]) {
|
|
26
|
+
const was = original[key], now = f[key], diff = {};
|
|
27
|
+
for (const k of new Set([...Object.keys(was), ...Object.keys(now)])) {
|
|
28
|
+
const a = own(was, k) ? was[k] : undefined, b = own(now, k) ? now[k] : undefined;
|
|
29
|
+
if (a === undefined && b === undefined) continue;
|
|
30
|
+
if (a !== undefined && b !== undefined && sameList(a, b)) continue;
|
|
31
|
+
// open_instances: none named = the entry leaves. narrow: undefined = lifted; [] = narrowed to nothing.
|
|
32
|
+
diff[k] = b === undefined || (key === "open_instances" && !b.length) ? null : [...b];
|
|
33
|
+
}
|
|
34
|
+
if (Object.keys(diff).length) out[key] = diff;
|
|
35
|
+
}
|
|
36
|
+
return Object.keys(out).length ? out : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** `away`: narrowed names it no longer reaches — listed (marked) only so they can be unticked; unticked, they leave. */
|
|
40
|
+
function Names({ id, all, value, onChange, away = new Set() }) {
|
|
41
|
+
const chosen = new Set(value || []);
|
|
42
|
+
return html`<div class="guest-names" id=${id} role="group">${all.map((n) => html`<label key=${n} class=${`check guest-name${away.has(n) ? " guest-away" : ""}`}>
|
|
43
|
+
<input type="checkbox" checked=${chosen.has(n)} onChange=${(e) => { const s = new Set(chosen); if (e.target.checked) s.add(n); else s.delete(n); onChange(all.filter((x) => s.has(x))); }} />
|
|
44
|
+
<span>${n}${away.has(n) ? html` <span class="note">${tn("guestNotReachable")}</span>` : null}</span></label>`)}</div>`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function GuestsCard({ ctx, staged, disabled = false }) {
|
|
48
|
+
const g = (ctx.fleet.web && ctx.fleet.web.guests) || {};
|
|
49
|
+
const original = useMemo(() => ({ enabled: g.enabled === true, chat: g.chat !== false, open_instances: copyLists(g.open_instances), narrow: copyLists(g.narrow) }), []);
|
|
50
|
+
const [f, setF] = useState(() => ({ ...original, open_instances: copyLists(original.open_instances), narrow: copyLists(original.narrow) }));
|
|
51
|
+
const [overview, setOverview] = useState(null), [failed, setFailed] = useState(false), [narrowing, setNarrowing] = useState(null), [msg, setMsg] = useState(null);
|
|
52
|
+
const load = async () => {
|
|
53
|
+
let r = null;
|
|
54
|
+
try { r = await api("/api/settings/guests"); } catch { r = null; }
|
|
55
|
+
if (r && r.ok && r.body) { setOverview(r.body); setFailed(false); } else setFailed(true);
|
|
56
|
+
};
|
|
57
|
+
useEffect(() => { load(); }, []);
|
|
58
|
+
const set = (patch) => setF((x) => ({ ...x, ...patch }));
|
|
59
|
+
const setList = (key, k, list) => setF((x) => { const m = { ...x[key] }; if (list === undefined) delete m[k]; else m[k] = list; return { ...x, [key]: m }; });
|
|
60
|
+
const review = () => {
|
|
61
|
+
const body = guestsBody(original, f);
|
|
62
|
+
if (body) ctx.stage("guests", { label: tn("guestsTitle"), impact: "now", request: { method: "PUT", url: "/api/settings/fleet/web", body: { guests: body } } });
|
|
63
|
+
else if (staged.has("guests")) ctx.unstage("guests");
|
|
64
|
+
setMsg(tn("readyApply"));
|
|
65
|
+
};
|
|
66
|
+
const signOut = async (guest) => {
|
|
67
|
+
const yes = await confirmDialog({ title: tn("guestSignOutTitle"), message: tn("guestSignOutConfirm", guest.name), confirmLabel: tn("guestSignOut"), danger: true });
|
|
68
|
+
if (!yes) return;
|
|
69
|
+
let r = null;
|
|
70
|
+
try { r = await api("/api/settings/guests/revoke", { method: "POST", body: JSON.stringify({ adapterId: guest.adapterId, userId: guest.userId }) }); } catch { r = null; }
|
|
71
|
+
if (r && r.ok) { toast(tn(r.body && r.body.durable === false ? "guestSignedOutNotDurable" : "guestSignedOut", guest.name)); load(); }
|
|
72
|
+
else toast(tn("guestSignOutFailed"), false);
|
|
73
|
+
};
|
|
74
|
+
const instances = (overview && overview.instances) || [];
|
|
75
|
+
const open = ((overview && overview.connections) || []).filter((c) => c.mode === "open");
|
|
76
|
+
const guests = (overview && overview.guests) || [];
|
|
77
|
+
const when = (ms) => (typeof ms === "number" ? tn("guestLastActive", new Date(ms).toLocaleString()) : tn("guestNotSignedIn"));
|
|
78
|
+
return html`<section class="card s-guests"><h3>${tn("guestsTitle")}</h3>
|
|
79
|
+
<p class="note">${tn("guestsHint")}</p>
|
|
80
|
+
<fieldset class="s-guest-fields" disabled=${disabled}>
|
|
81
|
+
<label class="check"><input id="guestsEnabled" type="checkbox" checked=${f.enabled} onChange=${(e) => set({ enabled: e.target.checked })} /><span>${tn("guestsEnabled")}</span></label>
|
|
82
|
+
<p class="note">${tn("guestsEnabledHint")}</p>
|
|
83
|
+
<label class="check"><input id="guestsChat" type="checkbox" checked=${f.chat} onChange=${(e) => set({ chat: e.target.checked })} /><span>${tn("guestsChat")}</span></label>
|
|
84
|
+
<p class="note">${tn("guestsChatHint")}</p>
|
|
85
|
+
${open.map((c) => html`<div key=${c.id} class="field guest-open"><span class="label">${tn("guestsOpenFor", c.id, PLATFORMS[c.type] || c.type)}</span>
|
|
86
|
+
<p class="note">${tn("guestsOpenHint")}</p>
|
|
87
|
+
<${Names} id=${`guestsOpen-${c.id}`} all=${instances} value=${own(f.open_instances, c.id) ? f.open_instances[c.id] : []} onChange=${(list) => setList("open_instances", c.id, list)} /></div>`)}
|
|
88
|
+
<h4>${tn("guestsList")}</h4>
|
|
89
|
+
${failed ? html`<p class="note" role="status">${tn("guestsLoadFailed")} <button type="button" class="btn btn-sm" onClick=${load}>${t("app.retry")}</button></p>`
|
|
90
|
+
: !overview ? html`<p class="note">${t("app.loading")}</p>`
|
|
91
|
+
: !guests.length ? html`<p class="note" id="guestsNone">${tn("guestsNone")}</p>`
|
|
92
|
+
: html`<ul class="guest-list">${guests.map((guest) => {
|
|
93
|
+
const k = accountKey(guest), narrowed = own(f.narrow, k) ? f.narrow[k] : undefined;
|
|
94
|
+
return html`<li key=${k} class="guest-row" data-account=${k}>
|
|
95
|
+
<div class="guest-who"><strong>${guest.name}</strong> <span class="note">${PLATFORMS[guest.platform] || guest.platform} · ${guest.userId}</span></div>
|
|
96
|
+
<div class="note guest-facts">${tn("guestFacts", guest.scope.length, guest.sessions, guest.devices)} · ${when(guest.lastSeen)}</div>
|
|
97
|
+
<div class="note guest-narrow">${narrowed === undefined ? tn("guestNotNarrowed") : tn("guestNarrowedTo", narrowed.length ? narrowed.join(", ") : tn("guestNothing"))}</div>
|
|
98
|
+
<div class="guest-actions">
|
|
99
|
+
<button type="button" class="btn btn-sm" aria-expanded=${narrowing === k ? "true" : "false"} onClick=${() => setNarrowing(narrowing === k ? null : k)}>${tn("guestNarrow")}</button>
|
|
100
|
+
${narrowed !== undefined ? html`<button type="button" class="btn btn-sm" onClick=${() => setList("narrow", k, undefined)}>${tn("guestNarrowLift")}</button>` : null}
|
|
101
|
+
${guest.sessions || guest.devices ? html`<button type="button" class="btn btn-sm btn-danger" onClick=${() => signOut(guest)}>${tn("guestSignOut")}</button>` : null}
|
|
102
|
+
</div>
|
|
103
|
+
${narrowing === k ? (() => {
|
|
104
|
+
// #2104 review: only what this account can reach (narrowing never widens); a narrowed name it no longer
|
|
105
|
+
// reaches is listed to be unticked, marked.
|
|
106
|
+
const reach = guest.reach || guest.scope, away = new Set((narrowed || []).filter((n) => !reach.includes(n)));
|
|
107
|
+
return html`<div class="guest-narrow-edit"><p class="note">${tn("guestNarrowHint")}</p>
|
|
108
|
+
${reach.length || away.size ? html`<${Names} id=${`guestNarrow-${k}`} all=${[...reach, ...away]} away=${away} value=${narrowed === undefined ? guest.scope : narrowed} onChange=${(list) => setList("narrow", k, list)} />`
|
|
109
|
+
: html`<p class="note">${tn("guestReachesNothing")}</p>`}</div>`;
|
|
110
|
+
})() : null}
|
|
111
|
+
</li>`;
|
|
112
|
+
})}</ul>`}
|
|
113
|
+
</fieldset>
|
|
114
|
+
<div class="save-row"><button type="button" class="btn btn-primary" id="guestsReview" disabled=${disabled} onClick=${review}>${tn("guestsReview")}</button>
|
|
115
|
+
${msg ? html`<span class="note" role="status">${msg}</span>` : null}</div>
|
|
116
|
+
</section>`;
|
|
117
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// #2056 B: a card title with a small "?" that opens the card's section of the docs, so Settings → Web appearance
|
|
2
|
+
// keeps one short line per control and the long explanations live in docs/web-dashboard(.zh-TW).md.
|
|
3
|
+
import { html } from "/assets/app-html.js";
|
|
4
|
+
import { lang, t } from "/assets/app-i18n.js";
|
|
5
|
+
import "./settings-strings.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* #2056 B: where each card's long explanation lives — a heading of docs/web-dashboard(.zh-TW).md on GitHub, so the
|
|
9
|
+
* page keeps one short line per control and a small "?" for the rest.
|
|
10
|
+
*/
|
|
11
|
+
const DOCS = { en: "https://github.com/songsid/AgEnD/blob/main/docs/web-dashboard.md", "zh-TW": "https://github.com/songsid/AgEnD/blob/main/docs/web-dashboard.zh-TW.md" };
|
|
12
|
+
export const LEARN_ANCHORS = {
|
|
13
|
+
colors: ["identity-colours", "識別色"], css: ["custom-css-settings--web-appearance", "自訂-css設定--網頁外觀"], themes: ["agents-themes", "agent-的主題"],
|
|
14
|
+
cards: ["identity-cards", "身分卡"], limits: ["card-upload-limits", "身分卡上傳限制"], widgets: ["widgets", "小工具"], mods: ["mods", "mod"],
|
|
15
|
+
effects: ["effects", "效果"], restore: ["restore-defaults", "還原預設值"],
|
|
16
|
+
};
|
|
17
|
+
export function learnHref(topic, language = lang()) {
|
|
18
|
+
const zh = language === "zh-TW";
|
|
19
|
+
return `${DOCS[zh ? "zh-TW" : "en"]}#${encodeURIComponent(LEARN_ANCHORS[topic][zh ? 1 : 0])}`;
|
|
20
|
+
}
|
|
21
|
+
/** A card title with its "?" link to the docs (a new tab; the page itself never fetches anything). */
|
|
22
|
+
export function CardTitle({ title, topic }) {
|
|
23
|
+
return html`<h3 class="s-ap-title"><span>${title}</span>${topic ? html`<a class="s-learn" href=${learnHref(topic)} target="_blank" rel="noopener noreferrer"
|
|
24
|
+
aria-label=${t("settings.learnMore", title)} title=${t("settings.learnMore", title)}>?</a>` : null}</h3>`;
|
|
25
|
+
}
|
|
@@ -97,15 +97,20 @@ export function effectiveSummary(live, config, fleetDefaults, classicDefaults) {
|
|
|
97
97
|
return { backend, model, effort };
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
/**
|
|
101
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Group by `groupFor(name)` — #2172: the sidebar's rule (instance-nav groupOf: its first team, else its first tag); the
|
|
102
|
+
* first tag when not given — teams first, then tags, "Other" last; within a group Running → Paused → Crashed → Stopped,
|
|
103
|
+
* then by name. `labelOf(group)` sorts by what the heading says.
|
|
104
|
+
*/
|
|
105
|
+
export function groupAgents(instances, live, filter, groupFor = (n) => groupOf(instances[n]), labelOf = (g) => g) {
|
|
102
106
|
const needle = String(filter || "").trim().toLowerCase();
|
|
103
107
|
let names = Object.keys(instances || {});
|
|
104
108
|
if (needle) names = names.filter((n) => n.toLowerCase().includes(needle) || shortName(n, instances[n]).toLowerCase().includes(needle));
|
|
105
109
|
const groups = {};
|
|
106
|
-
for (const n of names) (groups[
|
|
110
|
+
for (const n of names) (groups[groupFor(n)] ??= []).push(n);
|
|
107
111
|
const order = (n) => STATUS_ORDER[(live[n] && live[n].status) || "stopped"] ?? 9;
|
|
108
|
-
|
|
112
|
+
const team = (g) => g.charCodeAt(0) < 32; // a team's key (instance-nav TEAM_GROUP)
|
|
113
|
+
return Object.keys(groups).sort((a, b) => (a === "Other" ? 1 : b === "Other" ? -1 : team(a) !== team(b) ? (team(a) ? -1 : 1) : labelOf(a).localeCompare(labelOf(b))))
|
|
109
114
|
.map((g) => ({ group: g, names: groups[g].sort((a, b) => order(a) - order(b) || a.localeCompare(b)) }));
|
|
110
115
|
}
|
|
111
116
|
|
|
@@ -222,8 +227,12 @@ export function accessConfirmations(previousAccess, nextMode, nextUsers) {
|
|
|
222
227
|
return out;
|
|
223
228
|
}
|
|
224
229
|
|
|
225
|
-
/**
|
|
226
|
-
|
|
230
|
+
/**
|
|
231
|
+
* #2054: whether a ClassicBot room copies web chat, as the fleet decides it (ClassicChannelManager's load +
|
|
232
|
+
* classicWebEchoOn): an absent `web_echo` follows the fleet-wide `web.echo_to_channel` (default on); any other value
|
|
233
|
+
* echoes only when it is `true` (a `null`, `0` or string reads as off, as the manager reads it).
|
|
234
|
+
*/
|
|
235
|
+
export const classicWebEchoEffective = (room, fleet) => (room?.web_echo === undefined ? fleet?.web?.echo_to_channel !== false : room.web_echo === true);
|
|
227
236
|
|
|
228
237
|
// ── Developer YAML: the config subset's writer and reader, exactly as settings.html had them (round-trip verified) ──
|
|
229
238
|
export function toYaml(value, indent = 0) {
|
package/dist/ui/settings-mods.js
CHANGED
|
@@ -12,6 +12,7 @@ import { toast } from "/assets/ui-toast.js";
|
|
|
12
12
|
import { confirmDialog } from "/assets/ui-confirm.js";
|
|
13
13
|
import { startOwnerCssTrial } from "/assets/owner-css.js";
|
|
14
14
|
import { AgentWidget, checkWidget, fleetBinds, instanceBinds } from "/assets/agent-widget.js";
|
|
15
|
+
import { CardTitle } from "./settings-learn.js";
|
|
15
16
|
import "./settings-strings.js";
|
|
16
17
|
|
|
17
18
|
const tn = (k, ...v) => t(`settings.${k}`, ...v);
|
|
@@ -40,9 +41,9 @@ function why(body) {
|
|
|
40
41
|
|
|
41
42
|
/** The parts a plan offers, each with its default: everything but custom CSS. */
|
|
42
43
|
export function defaultChoice(plan) {
|
|
43
|
-
const c = { theme: false, identity: false, effects: false, ownerCss: false, widgets: {}, replace: false };
|
|
44
|
+
const c = { theme: false, identity: false, effects: false, ownerCss: false, ambient: false, widgets: {}, replace: false };
|
|
44
45
|
for (const it of plan.items) {
|
|
45
|
-
if (it.part === "theme" || it.part === "identity") c[it.part] = true;
|
|
46
|
+
if (it.part === "theme" || it.part === "identity" || it.part === "ambient") c[it.part] = true;
|
|
46
47
|
else if (it.part === "widget") c.widgets[it.slot] = true;
|
|
47
48
|
else if (it.part === "effect") c.effects = true;
|
|
48
49
|
// ownerCss stays false: "read it before turning it on" (r4 §4).
|
|
@@ -99,6 +100,19 @@ function PlanItem({ it, choice, setChoice, binds, fleet }) {
|
|
|
99
100
|
return html`<li class="s-mod-item">${tick(tn(`modPartWidget_${it.slot}`), !!choice.widgets[it.slot], (v) => setChoice({ ...choice, widgets: { ...choice.widgets, [it.slot]: v } }))}
|
|
100
101
|
<div class="s-mod-detail s-mod-widget">${w ? html`<${AgentWidget} slot=${it.slot} widget=${w} binds=${it.slot === "sidebar" ? fleet : binds} />` : null}</div></li>`;
|
|
101
102
|
}
|
|
103
|
+
if (it.part === "ambient") {
|
|
104
|
+
// #2117: each ambient as it will be drawn: its preset, glyphs (pictures shown) and colours; every field below.
|
|
105
|
+
const glyph = (g, i) => typeof g === "string" ? html`<span key=${i} class="s-mod-text">${g}</span>`
|
|
106
|
+
: html`<img key=${i} class="s-mod-glyph" src=${`/ui/mods/staged/${g.asset}`} alt="" loading="lazy" />`;
|
|
107
|
+
const one = (kind) => {
|
|
108
|
+
const a = it[kind];
|
|
109
|
+
if (!a || !Array.isArray(a.glyphs)) return null;
|
|
110
|
+
return html`<div key=${kind} class="s-mod-kv"><span class="k">${a.preset}</span><span class="v s-mod-sws">${a.glyphs.map(glyph)}${(a.colors || []).map((c) => html`<${Swatch} key=${c} color=${c} />`)}</span></div>`;
|
|
111
|
+
};
|
|
112
|
+
return html`<li class="s-mod-item">${tick(tn("modPartAmbient"), !!choice.ambient, (v) => setChoice({ ...choice, ambient: v }))}
|
|
113
|
+
<div class="s-mod-detail">${one("loop")}${one("click")}
|
|
114
|
+
<details class="s-mod-raw"><summary>${tn("modAllFields")}</summary><pre class="s-mod-css-text">${JSON.stringify({ loop: it.loop, click: it.click }, null, 2)}</pre></details></div></li>`;
|
|
115
|
+
}
|
|
102
116
|
if (it.part === "effect") {
|
|
103
117
|
return html`<li class="s-mod-item">${tick(tn("modPartEffect", it.title), choice.effects, (v) => setChoice({ ...choice, effects: v }))}
|
|
104
118
|
<div class="s-mod-detail"><span>${kib(it.bytes)} · ${tn("modRegions", it.regions.join(", "))}</span>${it.wants.length ? html`<span>${tn("modWants", it.wants.join(", "))}</span>` : null}</div></li>`;
|
|
@@ -119,7 +133,7 @@ function Plan({ staged, onDone }) {
|
|
|
119
133
|
const [target, setTarget] = useState("");
|
|
120
134
|
const [busy, setBusy] = useState(false);
|
|
121
135
|
const [error, setError] = useState("");
|
|
122
|
-
const toInstance = choice.theme || choice.identity || !!choice.widgets.chat_header || !!choice.widgets.details;
|
|
136
|
+
const toInstance = choice.theme || choice.identity || choice.ambient || !!choice.widgets.chat_header || !!choice.widgets.details;
|
|
123
137
|
const names = (app.instances || []).map((i) => i.name);
|
|
124
138
|
const install = async () => {
|
|
125
139
|
setBusy(true); setError("");
|
|
@@ -161,13 +175,13 @@ function Plan({ staged, onDone }) {
|
|
|
161
175
|
|
|
162
176
|
function ExportForm() {
|
|
163
177
|
const app = useStore(appStore);
|
|
164
|
-
const [f, setF] = useState({ instance: "", name: "", version: "1.0.0", title: "", theme: true, identity: true, header: true, details: true, sidebar: false, ownerCss: false });
|
|
178
|
+
const [f, setF] = useState({ instance: "", name: "", version: "1.0.0", title: "", theme: true, identity: true, header: true, details: true, ambient: true, sidebar: false, ownerCss: false });
|
|
165
179
|
const [error, setError] = useState("");
|
|
166
180
|
const set = (k) => (e) => setF({ ...f, [k]: e.target.type === "checkbox" ? e.target.checked : e.target.value });
|
|
167
181
|
const go = async () => {
|
|
168
182
|
setError("");
|
|
169
183
|
const body = { instance: f.instance || undefined, name: f.name, version: f.version, title: f.title,
|
|
170
|
-
include: { theme: f.theme, identity: f.identity, ownerCss: f.ownerCss, widgets: { chat_header: f.header, details: f.details, sidebar: f.sidebar } } };
|
|
184
|
+
include: { theme: f.theme, identity: f.identity, ambient: f.ambient, ownerCss: f.ownerCss, widgets: { chat_header: f.header, details: f.details, sidebar: f.sidebar } } };
|
|
171
185
|
let res;
|
|
172
186
|
try { res = await fetch("/ui/mods/export", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }); }
|
|
173
187
|
catch { setError(tn("modFailed", "network")); return; }
|
|
@@ -181,7 +195,7 @@ function ExportForm() {
|
|
|
181
195
|
return html`<details class="s-mod-export"><summary>${tn("modExport")}</summary>
|
|
182
196
|
<p class="sub">${tn("modExportLead")}</p>
|
|
183
197
|
<label class="field"><span>${tn("modExportInstance")}</span><select value=${f.instance} onChange=${set("instance")}><option value="">—</option>${(app.instances || []).map((i) => html`<option key=${i.name} value=${i.name}>${i.name}</option>`)}</select></label>
|
|
184
|
-
<div class="s-mod-boxes">${box("theme", tn("modPartTheme"))}${box("identity", tn("modPartIdentity"))}${box("header", tn("modPartWidget_chat_header"))}${box("details", tn("modPartWidget_details"))}${box("sidebar", tn("modPartWidget_sidebar"))}${box("ownerCss", tn("modPartCss"))}</div>
|
|
198
|
+
<div class="s-mod-boxes">${box("theme", tn("modPartTheme"))}${box("identity", tn("modPartIdentity"))}${box("header", tn("modPartWidget_chat_header"))}${box("details", tn("modPartWidget_details"))}${box("ambient", tn("modPartAmbient"))}${box("sidebar", tn("modPartWidget_sidebar"))}${box("ownerCss", tn("modPartCss"))}</div>
|
|
185
199
|
<label class="field"><span>${tn("modExportName")}</span><input value=${f.name} placeholder="my-look" onInput=${set("name")} /></label>
|
|
186
200
|
<label class="field"><span>${tn("modExportVersion")}</span><input value=${f.version} onInput=${set("version")} /></label>
|
|
187
201
|
<label class="field"><span>${tn("modExportTitle")}</span><input value=${f.title} onInput=${set("title")} /></label>
|
|
@@ -204,8 +218,8 @@ export function ModsCard() {
|
|
|
204
218
|
setState(b && Array.isArray(b.mods) ? { mods: b.mods.filter((m) => m && typeof m.id === "string"), staged, proposals } : { failed: true });
|
|
205
219
|
};
|
|
206
220
|
useEffect(() => { load(); }, []);
|
|
207
|
-
if (!state) return html`<section class="card s-appearance s-mods"
|
|
208
|
-
if (state.failed) return html`<section class="card s-appearance s-mods"
|
|
221
|
+
if (!state) return html`<section class="card s-appearance s-mods"><${CardTitle} title=${tn("modsTitle")} topic="mods" /><p class="sub">…</p></section>`;
|
|
222
|
+
if (state.failed) return html`<section class="card s-appearance s-mods"><${CardTitle} title=${tn("modsTitle")} topic="mods" /><p class="err">${tn("modsLoadFailed")}</p></section>`;
|
|
209
223
|
const stage = async () => {
|
|
210
224
|
const f = file.current && file.current.files && file.current.files[0];
|
|
211
225
|
if (!f) return;
|
|
@@ -238,7 +252,7 @@ export function ModsCard() {
|
|
|
238
252
|
if (!r || !r.ok) { toast(tn("modFailed", r ? r.status : "network"), false); return; }
|
|
239
253
|
download(await r.text(), `${m.name}-${m.version}.agendmod.json`);
|
|
240
254
|
};
|
|
241
|
-
return html`<section class="card s-appearance s-mods"
|
|
255
|
+
return html`<section class="card s-appearance s-mods"><${CardTitle} title=${tn("modsTitle")} topic="mods" />
|
|
242
256
|
<p class="sub">${tn("modsLead")}</p>
|
|
243
257
|
${state.staged ? html`<${Plan} key=${state.staged.stage} staged=${state.staged} onDone=${load} />` : html`<div class="s-mod-import">
|
|
244
258
|
<label class="btn"><${Icon} name="attach" size=${16} /><span>${tn("modChoose")}</span>
|
|
@@ -7,24 +7,24 @@ import { getAppEffects } from "./app-effects.js";
|
|
|
7
7
|
import { ownerEffectsStore, watchOwnerEffects, refreshOwnerEffects, saveOwnerEffects } from "/assets/owner-effect-prefs.js";
|
|
8
8
|
register("ownerEffects", {
|
|
9
9
|
en: {
|
|
10
|
-
title: "Your effect choices", operatorTitle: "Host-wide effect choices", person: "
|
|
11
|
-
operator: "
|
|
10
|
+
title: "Your effect choices", operatorTitle: "Host-wide effect choices", person: "Follows your sign-in; Discord and Telegram are separate.",
|
|
11
|
+
operator: "Shared by everyone signed in as the host.",
|
|
12
12
|
sidebar: "Sidebar", header: "Header", page: "Page", box: "Box", cursor: "Cursor", none: "None", savedChoice: "Saved choice",
|
|
13
13
|
unknown: "Choices are unavailable. Reload to try again.", changed: "This page can no longer access these choices. Reload and sign in again.",
|
|
14
14
|
unavailable: "No longer available", clear: "Clear", reload: "Reload choices", saved: "Saved.", failed: "Couldn't save. Reload choices and try again.",
|
|
15
15
|
savedRefresh: "Saved, but the choices couldn't be refreshed. Reload to check.", readOnly: "Sign in as an admin to change these choices.",
|
|
16
|
-
playback: "
|
|
17
|
-
pause: "Pause", resume: "Resume", running: "Running", cursorFailed: "The cursor could not be applied here. Clear or reload the choice to try again.", starting: "Starting", queued: "Waiting for a slot", paused: "Paused", off: "Playback is off or unavailable here.", budget: "Paused to
|
|
16
|
+
playback: "Effects don't require HTML-preview permission.",
|
|
17
|
+
pause: "Pause", resume: "Resume", running: "Running", cursorFailed: "The cursor could not be applied here. Clear or reload the choice to try again.", starting: "Starting", queued: "Waiting for a slot", paused: "Paused", off: "Playback is off or unavailable here.", budget: "Paused to keep the page responsive.", resumeFailed: "This effect cannot resume: the page or its safety history is not ready. Reload the page or choose another effect.",
|
|
18
18
|
},
|
|
19
19
|
"zh-TW": {
|
|
20
|
-
title: "你的效果選擇", operatorTitle: "主機共用效果選擇", person: "
|
|
21
|
-
operator: "
|
|
20
|
+
title: "你的效果選擇", operatorTitle: "主機共用效果選擇", person: "跟隨你的登入身分;Discord 與 Telegram 各自獨立。",
|
|
21
|
+
operator: "所有以主機身分登入的人共用。",
|
|
22
22
|
sidebar: "側邊欄", header: "頁首", page: "頁面", box: "區塊", cursor: "游標", none: "無", savedChoice: "已儲存的選擇",
|
|
23
23
|
unknown: "目前無法取得選擇,請重新載入再試。", changed: "這個頁面已無法存取選擇,請重新載入並登入。",
|
|
24
24
|
unavailable: "已無法使用", clear: "清除", reload: "重新載入選擇", saved: "已儲存。", failed: "無法儲存,請重新載入選擇再試。",
|
|
25
25
|
savedRefresh: "已儲存,但無法更新畫面;請重新載入確認。", readOnly: "請以管理員身分登入才能修改選擇。",
|
|
26
|
-
playback: "
|
|
27
|
-
pause: "暫停", resume: "恢復", running: "播放中", cursorFailed: "這裡無法套用游標。可清除或重新載入選擇後再試。", starting: "啟動中", queued: "等待空位", paused: "已暫停", off: "這裡已關閉播放,或無法播放效果。", budget: "
|
|
26
|
+
playback: "效果不需要 HTML 預覽權限。",
|
|
27
|
+
pause: "暫停", resume: "恢復", running: "播放中", cursorFailed: "這裡無法套用游標。可清除或重新載入選擇後再試。", starting: "啟動中", queued: "等待空位", paused: "已暫停", off: "這裡已關閉播放,或無法播放效果。", budget: "為維持頁面順暢,已暫停。", resumeFailed: "頁面或安全紀錄尚未就緒,無法恢復此效果。請重新載入頁面,或選擇其他效果。",
|
|
28
28
|
},
|
|
29
29
|
});
|
|
30
30
|
const text = key => t(`ownerEffects.${key}`);
|
|
@@ -39,7 +39,7 @@ export function OwnerEffects({ catalog, busy = false }) {
|
|
|
39
39
|
useEffect(() => { life.current.alive = true; const stop = watchOwnerEffects(); return () => { life.current.alive = false; life.current.seq++; stop(); }; }, []);
|
|
40
40
|
useEffect(() => {
|
|
41
41
|
let live = true; const entry = getAppEffects();
|
|
42
|
-
const pull = () => { if (!live) return; const gates = entry?.gates(); setRuntime({ entry, snapshot: entry?.api?.ownerSnapshot?.(), cursor: entry?.cursorSnapshot?.(), available: !!entry && !entry.dead && gates.enabled && gates.show && gates.
|
|
42
|
+
const pull = () => { if (!live) return; const gates = entry?.gates(); setRuntime({ entry, snapshot: entry?.api?.ownerSnapshot?.(), cursor: entry?.cursorSnapshot?.(), available: !!entry && !entry.dead && gates.enabled && gates.show && gates.transportReady && !gates.safe && !gates.publicLink && !gates.reducedMotion }); };
|
|
43
43
|
const off = entry?.subscribe(pull), end = entry?.onEnd(pull); pull();
|
|
44
44
|
return () => { live = false; off?.(); end?.(); };
|
|
45
45
|
}, []);
|