@oharness/server 0.12.0 → 0.14.0
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/i18n.js +204 -0
- package/dist/i18n.js.map +1 -1
- package/dist/page/client/events.js +8 -3
- package/dist/page/client/events.js.map +1 -1
- package/dist/page/client/panels.js +225 -1
- package/dist/page/client/panels.js.map +1 -1
- package/dist/page/client/routines.js +67 -4
- package/dist/page/client/routines.js.map +1 -1
- package/dist/page/client/shell.js +8 -1
- package/dist/page/client/shell.js.map +1 -1
- package/dist/page/client/state.js +15 -2
- package/dist/page/client/state.js.map +1 -1
- package/dist/page/client/tasks.js +153 -7
- package/dist/page/client/tasks.js.map +1 -1
- package/dist/page/client/usage.js +6 -1
- package/dist/page/client/usage.js.map +1 -1
- package/dist/page/style.js +90 -1
- package/dist/page/style.js.map +1 -1
- package/dist/server.js +58 -5
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
|
@@ -441,6 +441,10 @@ async function showMemoryPage(id) {
|
|
|
441
441
|
}
|
|
442
442
|
// Nothing to tidy is not worth a row.
|
|
443
443
|
if (findings.length || consolidate.lastAuto) panel.appendChild(tidy);
|
|
444
|
+
// Supplied by the dreaming plugin; absent when it is switched off. Its own
|
|
445
|
+
// page holds the detail; here, one line and the way there.
|
|
446
|
+
const dream = result.sections && result.sections.dreaming;
|
|
447
|
+
if (dream) panel.appendChild(dreamCard(dream));
|
|
444
448
|
const editor = (note, extra) => {
|
|
445
449
|
const form = el("form", "memory-editor"), summary = el("input", ""), body = el("textarea", ""), tags = el("input", "");
|
|
446
450
|
summary.placeholder = t("memory.summaryPlaceholder"); summary.value = note?.summary || ""; summary.required = true;
|
|
@@ -604,7 +608,7 @@ async function showMemoryPage(id) {
|
|
|
604
608
|
* has a row of its own on the rail as well as Settings', and lights that one
|
|
605
609
|
* whichever way it was reached.
|
|
606
610
|
*/
|
|
607
|
-
const SUB_PANELS = { usage: "nav-usage", audit: "nav-settings", permissions: "nav-settings" };
|
|
611
|
+
const SUB_PANELS = { usage: "nav-usage", audit: "nav-settings", permissions: "nav-settings", dreaming: "nav-settings" };
|
|
608
612
|
|
|
609
613
|
/** Which panel the panel view is currently showing, sub-panels included. */
|
|
610
614
|
let shownPanel = null;
|
|
@@ -617,6 +621,226 @@ let shownPanel = null;
|
|
|
617
621
|
* left intact underneath it, and the composer is hidden — there is nothing to
|
|
618
622
|
* send a message to while reading a list of providers.
|
|
619
623
|
*/
|
|
624
|
+
/** Spent money, at the precision a small amount needs. */
|
|
625
|
+
function dreamCost(usd) {
|
|
626
|
+
return "$" + (usd || 0).toFixed(usd > 0 && usd < 0.01 ? 4 : 2);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** Whether a timestamp falls in this local calendar month. */
|
|
630
|
+
function inThisMonth(at) {
|
|
631
|
+
const d = new Date(at), now = new Date();
|
|
632
|
+
return d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth();
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/** The Memory page's line about dreaming, and the way to its page. */
|
|
636
|
+
function dreamCard(dream) {
|
|
637
|
+
const card = el("div", "row-card dream-card");
|
|
638
|
+
const head = el("div", "row-head");
|
|
639
|
+
head.appendChild(el("span", "row-title", t("dream.title")));
|
|
640
|
+
head.appendChild(el("span", "memory-dream-tag " + (dream.auto ? "apply" : ""), t(dream.auto ? "dream.auto" : "dream.manual")));
|
|
641
|
+
const open = el("button", "ghost", t("dream.open")); open.type = "button";
|
|
642
|
+
open.onclick = () => { setNav("dreaming"); void showDreaming(); };
|
|
643
|
+
head.appendChild(open);
|
|
644
|
+
card.appendChild(head);
|
|
645
|
+
const facts = [];
|
|
646
|
+
if ((dream.proposals || []).length) facts.push(t("dream.waitingCount", { n: dream.proposals.length }));
|
|
647
|
+
facts.push(t("dream.monthSpend", { tokens: formatTokens((dream.spend && dream.spend.tokens) || 0), cost: dreamCost(dream.spend && dream.spend.costUsd) }));
|
|
648
|
+
card.appendChild(el("div", "muted small", facts.join(" · ")));
|
|
649
|
+
return card;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/** The switch for whether dreaming runs on its own. */
|
|
653
|
+
function dreamAutoSwitch(on, onChange) {
|
|
654
|
+
const button = el("button", "switch");
|
|
655
|
+
button.type = "button";
|
|
656
|
+
button.setAttribute("role", "switch");
|
|
657
|
+
button.setAttribute("aria-checked", on ? "true" : "false");
|
|
658
|
+
button.setAttribute("aria-label", t("dream.autoSwitch"));
|
|
659
|
+
button.title = t(on ? "dream.autoOnHint" : "dream.autoOffHint");
|
|
660
|
+
button.addEventListener("click", (event) => {
|
|
661
|
+
// Inside a row's summary, a click would also open or close the row.
|
|
662
|
+
event.preventDefault(); event.stopPropagation();
|
|
663
|
+
button.disabled = true;
|
|
664
|
+
void Promise.resolve(onChange(!on));
|
|
665
|
+
});
|
|
666
|
+
return button;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/** When the next automatic dream happens, scope by scope, in words. */
|
|
670
|
+
function dreamNext(dream) {
|
|
671
|
+
if (!dream.auto) return t("dream.next.manual");
|
|
672
|
+
const settings = dream.settings || {};
|
|
673
|
+
const parts = [];
|
|
674
|
+
for (const scope of (dream.schedule && dream.schedule.scopes) || []) {
|
|
675
|
+
let state;
|
|
676
|
+
if (scope.dreamedToday) state = t("dream.next.doneToday");
|
|
677
|
+
else if (!scope.waiting) state = t("dream.next.nothing");
|
|
678
|
+
else if (scope.waiting >= settings.minClaims || (scope.oldestAt && Date.now() - scope.oldestAt >= settings.maxWaitHours * 3600000)) state = t("dream.next.due");
|
|
679
|
+
else {
|
|
680
|
+
const hours = Math.max(1, Math.ceil((scope.oldestAt + settings.maxWaitHours * 3600000 - Date.now()) / 3600000));
|
|
681
|
+
state = t("dream.next.gathering", { n: scope.waiting, min: settings.minClaims, hours });
|
|
682
|
+
}
|
|
683
|
+
parts.push(t("memory.dream.scope." + scope.scope) + ": " + state);
|
|
684
|
+
}
|
|
685
|
+
return parts.join(" · ");
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/** Before and after, side by side, for one change. */
|
|
689
|
+
function dreamDiff(change) {
|
|
690
|
+
const box = el("div", "dream-diff");
|
|
691
|
+
const side = (label, notes, empty) => {
|
|
692
|
+
const col = el("div", "dream-diff-side");
|
|
693
|
+
col.appendChild(el("div", "muted small", label));
|
|
694
|
+
if (!notes.length) col.appendChild(el("div", "muted small", empty));
|
|
695
|
+
for (const note of notes) {
|
|
696
|
+
if (note.removed) col.appendChild(el("div", "muted small", t("dream.diff.removed")));
|
|
697
|
+
else if (note.editedSince) col.appendChild(el("div", "muted small", t("dream.diff.editedSince")));
|
|
698
|
+
else col.appendChild(el("pre", "memory-dream-body", note.summary + "\n\n" + note.body));
|
|
699
|
+
}
|
|
700
|
+
return col;
|
|
701
|
+
};
|
|
702
|
+
box.appendChild(side(t("memory.dream.before"), (change.before || []).filter((note) => note.body), t("dream.diff.none")));
|
|
703
|
+
box.appendChild(side(t("memory.dream.after"), change.after || [], t("dream.diff.none")));
|
|
704
|
+
return box;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Dreaming's own page: whether it runs and when next, what this month cost,
|
|
709
|
+
* the suggestions waiting, and every run, each with what it changed.
|
|
710
|
+
*/
|
|
711
|
+
async function showDreaming() {
|
|
712
|
+
const session = activeId;
|
|
713
|
+
const page = [...pluginPages.values()].find((one) => one.renderer === "memory");
|
|
714
|
+
const result = page ? await getJson("/plugin-page?id=" + encodeURIComponent(page.id) + "&session=" + encodeURIComponent(session || "")) : {};
|
|
715
|
+
if (shownPanel !== "dreaming" || activeId !== session) return;
|
|
716
|
+
panelBody.replaceChildren(); panelNotes.replaceChildren();
|
|
717
|
+
const panel = el("div", "panel memory-panel dream-panel"); panelBody.appendChild(panel);
|
|
718
|
+
const dream = result.sections && result.sections.dreaming;
|
|
719
|
+
const head = el("div", "dream-head");
|
|
720
|
+
head.appendChild(el("h2", "", t("dream.title")));
|
|
721
|
+
panel.appendChild(head);
|
|
722
|
+
if (!dream) { panel.appendChild(el("p", "muted", t("dream.off"))); return; }
|
|
723
|
+
head.appendChild(el("span", "dream-spacer"));
|
|
724
|
+
// Whether it runs on its own: the user's choice, kept in their plugin config.
|
|
725
|
+
const autoLabel = el("label", "dream-auto");
|
|
726
|
+
autoLabel.appendChild(el("span", "small", t("dream.autoSwitch")));
|
|
727
|
+
const autoSwitch = dreamAutoSwitch(dream.auto, (next) => act({ action: "setAuto", auto: next }));
|
|
728
|
+
autoLabel.appendChild(autoSwitch);
|
|
729
|
+
head.appendChild(autoLabel);
|
|
730
|
+
const act = async (input) => {
|
|
731
|
+
try {
|
|
732
|
+
const reply = await api("/plugin-page", { page: page.id, session, input: { action: "section", section: "dreaming", input } }, 120000);
|
|
733
|
+
if (reply.error) throw Object.assign(new Error(reply.error), { code: reply.code, params: reply.params });
|
|
734
|
+
} catch (error) {
|
|
735
|
+
if (shownPanel === "dreaming") panelNotes.appendChild(el("p", "small memory-error", memoryText(error)));
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (shownPanel === "dreaming" && activeId === session) await showDreaming();
|
|
739
|
+
};
|
|
740
|
+
const now = el("button", "ghost", dream.running ? t("memory.dream.running") : t("memory.dream.now"));
|
|
741
|
+
now.type = "button"; now.disabled = !!dream.running;
|
|
742
|
+
now.onclick = () => { now.disabled = true; now.textContent = t("memory.dream.running"); void act({ action: "dreamNow" }); };
|
|
743
|
+
head.appendChild(now);
|
|
744
|
+
panel.appendChild(el("p", "muted small", dreamNext(dream)));
|
|
745
|
+
if (dream.error) panel.appendChild(el("p", "small memory-error", dream.error));
|
|
746
|
+
if (dream.running) setTimeout(() => { if (shownPanel === "dreaming" && activeId === session) void showDreaming(); }, 3000);
|
|
747
|
+
|
|
748
|
+
const runs = dream.runs || [], log = dream.log || [], proposals = dream.proposals || [];
|
|
749
|
+
const monthRuns = runs.filter((run) => inThisMonth(run.at));
|
|
750
|
+
const monthLog = log.filter((item) => inThisMonth(item.at));
|
|
751
|
+
const metrics = el("div", "dream-metrics");
|
|
752
|
+
const metric = (label, value, sub) => {
|
|
753
|
+
const box = el("div", "dream-metric");
|
|
754
|
+
box.appendChild(el("div", "muted small", label));
|
|
755
|
+
box.appendChild(el("div", "dream-metric-value", value));
|
|
756
|
+
box.appendChild(el("div", "muted small", sub));
|
|
757
|
+
metrics.appendChild(box);
|
|
758
|
+
};
|
|
759
|
+
metric(t("dream.metric.applied"), String(monthLog.length), t("dream.metric.undone", { n: monthLog.filter((item) => item.undone).length }));
|
|
760
|
+
metric(t("dream.metric.waiting"), String(proposals.length), t("dream.metric.suggestions"));
|
|
761
|
+
metric(t("dream.metric.spent"), formatTokens((dream.spend && dream.spend.tokens) || 0), t("dream.metric.cost", { cost: dreamCost(dream.spend && dream.spend.costUsd) }));
|
|
762
|
+
metric(t("dream.metric.runs"), String(monthRuns.length), t("dream.metric.read", { n: monthRuns.reduce((sum, run) => sum + (run.digested || 0), 0) }));
|
|
763
|
+
panel.appendChild(metrics);
|
|
764
|
+
const settings = dream.settings || {};
|
|
765
|
+
panel.appendChild(el("p", "muted small", t("dream.budgetToday", { used: formatTokens((dream.schedule && dream.schedule.spentToday) || 0), limit: formatTokens(settings.dailyTokens || 0) })));
|
|
766
|
+
|
|
767
|
+
const evidence = (row, claims) => {
|
|
768
|
+
if (!claims || !claims.length) return;
|
|
769
|
+
const list = el("ul", "memory-dream-evidence small");
|
|
770
|
+
for (const claim of claims) list.appendChild(el("li", "", t("memory.dream.kind." + claim.kind) + " · " + new Date(claim.at).toLocaleDateString(LOCALE) + " · " + (claim.quote ? "“" + claim.quote + "”" : claim.text)));
|
|
771
|
+
row.appendChild(list);
|
|
772
|
+
};
|
|
773
|
+
const changeRow = (item) => {
|
|
774
|
+
const row = el("div", "dream-change");
|
|
775
|
+
const line = el("div", "dream-line");
|
|
776
|
+
line.appendChild(el("span", "memory-dream-tag " + (item.undone ? "" : "apply"), item.undone ? t("memory.dream.undone") : t("memory.dream.op." + item.op)));
|
|
777
|
+
line.appendChild(el("span", "dream-grow", t("memory.dream.scope." + item.scope) + " · " + item.summary));
|
|
778
|
+
const compare = el("button", "ghost dream-small", t("dream.compare")); compare.type = "button";
|
|
779
|
+
let diff;
|
|
780
|
+
compare.onclick = () => { if (diff) { diff.remove(); diff = undefined; } else { diff = dreamDiff(item); row.appendChild(diff); } };
|
|
781
|
+
line.appendChild(compare);
|
|
782
|
+
if (!item.undone) {
|
|
783
|
+
const undo = el("button", "ghost dream-small", t("memory.dream.undo")); undo.type = "button";
|
|
784
|
+
undo.onclick = () => { undo.disabled = true; void act({ action: "undo", scope: item.scope, id: item.id }); };
|
|
785
|
+
line.appendChild(undo);
|
|
786
|
+
}
|
|
787
|
+
row.appendChild(line);
|
|
788
|
+
evidence(row, item.evidence);
|
|
789
|
+
return row;
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
panel.appendChild(el("h3", "dream-section", t("dream.suggestions", { n: proposals.length })));
|
|
793
|
+
if (!proposals.length) panel.appendChild(el("p", "muted small", t("dream.noSuggestions")));
|
|
794
|
+
for (const item of proposals) {
|
|
795
|
+
const row = el("details", "dream-change");
|
|
796
|
+
const line = el("summary", "dream-line");
|
|
797
|
+
line.appendChild(el("span", "memory-dream-tag review", t("memory.dream.op." + item.op)));
|
|
798
|
+
line.appendChild(el("span", "dream-grow", t("memory.dream.scope." + item.scope) + " · " + (item.summary || (item.targets[0] && item.targets[0].summary) || "")));
|
|
799
|
+
if (item.why) line.appendChild(el("span", "muted small", t("memory.dream.why." + item.why)));
|
|
800
|
+
row.appendChild(line);
|
|
801
|
+
if (item.reason) row.appendChild(el("p", "small", item.reason));
|
|
802
|
+
for (const target of item.targets || []) if (target.body) { row.appendChild(el("div", "muted small", t("memory.dream.before") + " · " + target.summary)); row.appendChild(el("pre", "memory-dream-body before", target.body)); }
|
|
803
|
+
if (item.body) { row.appendChild(el("div", "muted small", t("memory.dream.proposed") + " · " + item.summary + (item.tags && item.tags.length ? " · #" + item.tags.join(" #") : ""))); row.appendChild(el("pre", "memory-dream-body", item.body)); }
|
|
804
|
+
evidence(row, item.evidence);
|
|
805
|
+
const buttons = el("div", "memory-actions");
|
|
806
|
+
if (item.op === "create" || item.op === "update" || item.op === "merge") {
|
|
807
|
+
const apply = el("button", "ghost", t("memory.dream.applyOne")); apply.type = "button";
|
|
808
|
+
apply.onclick = () => { apply.disabled = true; void act({ action: "applyProposal", scope: item.scope, id: item.id }); };
|
|
809
|
+
buttons.appendChild(apply);
|
|
810
|
+
}
|
|
811
|
+
const dismiss = el("button", "ghost", t("memory.dream.dismiss")); dismiss.type = "button";
|
|
812
|
+
dismiss.onclick = () => { void act({ action: "dismissProposal", scope: item.scope, id: item.id }); };
|
|
813
|
+
buttons.appendChild(dismiss);
|
|
814
|
+
row.appendChild(buttons);
|
|
815
|
+
panel.appendChild(row);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
panel.appendChild(el("h3", "dream-section", t("dream.history")));
|
|
819
|
+
if (!runs.length) panel.appendChild(el("p", "muted small", t("dream.noHistory")));
|
|
820
|
+
for (const run of runs) {
|
|
821
|
+
const row = el("details", "dream-run");
|
|
822
|
+
const line = el("summary", "dream-line");
|
|
823
|
+
line.appendChild(el("span", "dream-when", new Date(run.at).toLocaleString(LOCALE, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" })));
|
|
824
|
+
const bad = run.outcome === "refused" || run.outcome === "failed" || run.outcome === "budget";
|
|
825
|
+
line.appendChild(el("span", "memory-dream-tag " + (bad ? "review" : ""), t("dream.trigger." + run.trigger)));
|
|
826
|
+
const counts = t("dream.run.counts", { read: run.digested, applied: run.applied, suggested: run.proposals });
|
|
827
|
+
line.appendChild(el("span", "dream-grow", run.outcome === "no-call" ? t("dream.run.noCall") : bad ? t("dream.run." + run.outcome) + " · " + counts : counts));
|
|
828
|
+
line.appendChild(el("span", "muted small", formatTokens((run.tokens.input || 0) + (run.tokens.output || 0)) + " · " + dreamCost(run.costUsd)));
|
|
829
|
+
row.appendChild(line);
|
|
830
|
+
const changes = log.filter((item) => item.run === run.id);
|
|
831
|
+
if (!changes.length) row.appendChild(el("p", "muted small", t(run.outcome === "refused" ? "dream.run.refusedDetail" : "dream.run.noChanges")));
|
|
832
|
+
for (const item of changes) row.appendChild(changeRow(item));
|
|
833
|
+
for (const error of run.errors || []) row.appendChild(el("p", "small memory-error", error));
|
|
834
|
+
if (run.model) row.appendChild(el("p", "muted small", t("dream.run.model", { model: run.model, seconds: run.seconds })));
|
|
835
|
+
panel.appendChild(row);
|
|
836
|
+
}
|
|
837
|
+
const byHand = log.filter((item) => item.fromProposal);
|
|
838
|
+
if (byHand.length) {
|
|
839
|
+
panel.appendChild(el("h3", "dream-section", t("dream.appliedByYou")));
|
|
840
|
+
for (const item of byHand) panel.appendChild(changeRow(item));
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
620
844
|
function setNav(active) {
|
|
621
845
|
// Every nav destination funnels through here, so one call covers them all.
|
|
622
846
|
track("panel_opened", { panel: active });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panels.js","sourceRoot":"","sources":["../../../src/page/client/panels.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"panels.js","sourceRoot":"","sources":["../../../src/page/client/panels.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAq3B/B,CAAC"}
|
|
@@ -400,6 +400,21 @@ async function showSettings(opts) {
|
|
|
400
400
|
return box;
|
|
401
401
|
};
|
|
402
402
|
|
|
403
|
+
// A row of small figures inside an opened row: a label, a value, and a line
|
|
404
|
+
// under it. What the row's page is about, at a glance, without opening it.
|
|
405
|
+
const shortWhen = (at) => new Date(at).toLocaleString(LOCALE, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
406
|
+
const stats = (box, items) => {
|
|
407
|
+
const grid = el("div", "setting-stats");
|
|
408
|
+
for (const item of items) {
|
|
409
|
+
const tile = el("div", "setting-stat" + (item.tone ? " " + item.tone : ""));
|
|
410
|
+
tile.appendChild(el("div", "setting-stat-label", item.label));
|
|
411
|
+
tile.appendChild(el("div", "setting-stat-value", item.value));
|
|
412
|
+
if (item.sub) tile.appendChild(el("div", "setting-stat-sub", item.sub));
|
|
413
|
+
grid.appendChild(tile);
|
|
414
|
+
}
|
|
415
|
+
box.appendChild(grid);
|
|
416
|
+
};
|
|
417
|
+
|
|
403
418
|
// A labelled block inside an opened row: what the label names, printed as
|
|
404
419
|
// typed. Used for the permission rules and the settings files, both of
|
|
405
420
|
// which are lists someone reads once and closes again.
|
|
@@ -501,12 +516,60 @@ async function showSettings(opts) {
|
|
|
501
516
|
: !result.enabled ? "off"
|
|
502
517
|
: result.syncing ? "syncing"
|
|
503
518
|
: (result.errors && result.errors.length) || result.error ? "error" : "on";
|
|
504
|
-
box.appendChild(el("div", "small", t("memory.state." + state)));
|
|
505
519
|
const entries = result.entries || [];
|
|
506
520
|
const project = entries.filter((entry) => entry.scope !== "user").length;
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
: t("
|
|
521
|
+
const tidy = result.reviewTotal || (result.review || []).length;
|
|
522
|
+
stats(box, [
|
|
523
|
+
{ label: t("settings.stat.sync"), value: t("settings.stat.syncState." + state), sub: result.lastSync ? t("settings.stat.syncedAgo", { when: shortWhen(result.lastSync) }) : "", tone: state === "error" || state === "account" ? "warn" : "" },
|
|
524
|
+
{ label: t("settings.stat.notes"), value: String(entries.length), sub: result.userAvailable ? t("settings.stat.notesSplit", { project, user: entries.length - project }) : "" },
|
|
525
|
+
{ label: t("settings.stat.tidy"), value: String(tidy), sub: t(tidy ? "settings.stat.tidySome" : "settings.stat.tidyNone") },
|
|
526
|
+
]);
|
|
527
|
+
});
|
|
528
|
+
// Dreaming builds on memory and shows on its page data; its row hides
|
|
529
|
+
// itself when the plugin is off.
|
|
530
|
+
const openDream = el("button", "ghost", t("settings.memoryOpen"));
|
|
531
|
+
openDream.type = "button";
|
|
532
|
+
openDream.addEventListener("click", () => { setNav("dreaming"); void showDreaming(); });
|
|
533
|
+
// The switch is drawn once the state is known; until then, a disabled one.
|
|
534
|
+
// One read draws both the switch and, once the row has been opened, its
|
|
535
|
+
// tiles, so flipping the switch updates what the tiles say too.
|
|
536
|
+
const autoSlot = el("span", "dream-auto-slot");
|
|
537
|
+
autoSlot.appendChild(Object.assign(el("button", "switch"), { type: "button", disabled: true }));
|
|
538
|
+
const dreamRow = field(t("dream.title"), "", t("dream.settingsHint"), autoSlot, openDream);
|
|
539
|
+
let dreamTiles;
|
|
540
|
+
const fillDream = (box, dream) => {
|
|
541
|
+
box.replaceChildren();
|
|
542
|
+
const last = (dream.runs || [])[0];
|
|
543
|
+
const waiting = (dream.proposals || []).length;
|
|
544
|
+
stats(box, [
|
|
545
|
+
{ label: t("dream.stat.mode"), value: t(dream.auto ? "dream.stat.modeAuto" : "dream.stat.modeManual"), sub: t(dream.auto ? "dream.stat.modeAutoSub" : "dream.stat.modeManualSub"), tone: dream.auto ? "ok" : "" },
|
|
546
|
+
{ label: t("dream.stat.last"), value: last ? shortWhen(last.at) : t("dream.stat.never"), sub: last ? t("dream.stat.lastSub", { applied: last.applied, suggested: last.proposals }) : "" },
|
|
547
|
+
{ label: t("dream.stat.month"), value: formatTokens((dream.spend && dream.spend.tokens) || 0) + " tokens", sub: dreamCost(dream.spend && dream.spend.costUsd) },
|
|
548
|
+
...(waiting ? [{ label: t("dream.stat.waiting"), value: String(waiting), sub: t("dream.metric.suggestions"), tone: "warn" }] : []),
|
|
549
|
+
]);
|
|
550
|
+
};
|
|
551
|
+
const readDream = async () => {
|
|
552
|
+
const result = await getJson("/plugin-page?id=" + encodeURIComponent(page.id) + "&session=" + encodeURIComponent(activeId || ""));
|
|
553
|
+
const dream = result.sections && result.sections.dreaming;
|
|
554
|
+
// The field helper returns the row body; the row itself is its parent.
|
|
555
|
+
if (!dream) dreamRow.parentNode.hidden = true;
|
|
556
|
+
return dream;
|
|
557
|
+
};
|
|
558
|
+
const drawAuto = async () => {
|
|
559
|
+
const dream = await readDream();
|
|
560
|
+
if (!dream) return;
|
|
561
|
+
autoSlot.replaceChildren(dreamAutoSwitch(dream.auto, async (next) => {
|
|
562
|
+
await api("/plugin-page", { page: page.id, session: activeId, input: { action: "section", section: "dreaming", input: { action: "setAuto", auto: next } } });
|
|
563
|
+
await drawAuto();
|
|
564
|
+
}));
|
|
565
|
+
if (dreamTiles) fillDream(dreamTiles, dream);
|
|
566
|
+
};
|
|
567
|
+
void drawAuto();
|
|
568
|
+
preview(dreamRow, async (box) => {
|
|
569
|
+
const dream = await readDream();
|
|
570
|
+
if (!dream) return void box.replaceChildren();
|
|
571
|
+
dreamTiles = box;
|
|
572
|
+
fillDream(box, dream);
|
|
510
573
|
});
|
|
511
574
|
}
|
|
512
575
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routines.js","sourceRoot":"","sources":["../../../src/page/client/routines.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"routines.js","sourceRoot":"","sources":["../../../src/page/client/routines.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kFA21CgD,CAAC"}
|
|
@@ -51,6 +51,7 @@ function repaintPanel() {
|
|
|
51
51
|
else if (shownPanel === "audit") void showAudit();
|
|
52
52
|
else if (shownPanel === "permissions") void showPermissions();
|
|
53
53
|
else if (shownPanel?.startsWith("nav-plugin-")) void showMemoryPage(shownPanel.slice("nav-plugin-".length));
|
|
54
|
+
else if (shownPanel === "dreaming") void showDreaming();
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
async function loadProjects() {
|
|
@@ -854,7 +855,13 @@ function showUpdatePrompt(prompt) {
|
|
|
854
855
|
head.appendChild(el("div", "upd-title", title));
|
|
855
856
|
dialog.appendChild(head);
|
|
856
857
|
|
|
857
|
-
|
|
858
|
+
// The notes are build/release-notes.md as written, so headings, lists and
|
|
859
|
+
// emphasis render rather than show their markers.
|
|
860
|
+
if (kind === "download" && prompt.notes) {
|
|
861
|
+
const notes = el("div", "upd-notes");
|
|
862
|
+
notes.appendChild(renderMarkdown(prompt.notes));
|
|
863
|
+
dialog.appendChild(notes);
|
|
864
|
+
}
|
|
858
865
|
const body =
|
|
859
866
|
kind === "download"
|
|
860
867
|
? t("update.prompt.availableBody", vars)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../../src/page/client/shell.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../../src/page/client/shell.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAkgCzB,CAAC"}
|
|
@@ -578,8 +578,18 @@ async function deleteProject(cwd, name) {
|
|
|
578
578
|
let sessionRows = [];
|
|
579
579
|
|
|
580
580
|
async function loadSessions() {
|
|
581
|
+
const revision = sidebarTaskRevision;
|
|
582
|
+
const request = ++sessionListRequest;
|
|
581
583
|
const body = await getJson("/sessions");
|
|
584
|
+
if (request !== sessionListRequest) return;
|
|
585
|
+
if (body.error) return void addNotice(body.error, "err");
|
|
582
586
|
sessionRows = body.sessions || [];
|
|
587
|
+
for (const s of sessionRows) {
|
|
588
|
+
const cached = sidebarTasks.get(s.id);
|
|
589
|
+
if (!cached || cached.revision <= revision) {
|
|
590
|
+
sidebarTasks.set(s.id, { tasks: s.tasks || [], revision });
|
|
591
|
+
}
|
|
592
|
+
}
|
|
583
593
|
sessionOrder = body.order || {};
|
|
584
594
|
paintSessions();
|
|
585
595
|
}
|
|
@@ -707,7 +717,7 @@ function paintSessions() {
|
|
|
707
717
|
if (open) {
|
|
708
718
|
const all = showingAll.has(ROUTINE_GROUP) || scheduled.length <= PREVIEW + 1;
|
|
709
719
|
for (const s of all ? scheduled : scheduled.slice(0, PREVIEW)) {
|
|
710
|
-
sessionList.appendChild(
|
|
720
|
+
sessionList.appendChild(sessionBlock(s, true));
|
|
711
721
|
}
|
|
712
722
|
if (!all) sessionList.appendChild(showMore(ROUTINE_GROUP, true));
|
|
713
723
|
}
|
|
@@ -730,7 +740,7 @@ function paintSessions() {
|
|
|
730
740
|
const shown = all
|
|
731
741
|
? list
|
|
732
742
|
: list.filter((s, i) => i < PREVIEW || s.pinned);
|
|
733
|
-
for (const s of shown) sessionList.appendChild(
|
|
743
|
+
for (const s of shown) sessionList.appendChild(sessionBlock(s, named));
|
|
734
744
|
if (!all) sessionList.appendChild(showMore(cwd, named));
|
|
735
745
|
}
|
|
736
746
|
}
|
|
@@ -1210,6 +1220,9 @@ async function deleteSession(id) {
|
|
|
1210
1220
|
// leaves them pointing at nothing. Said once, rather than blocked.
|
|
1211
1221
|
if (body.forks) addNotice(t("session.deletedForks", { n: body.forks }), "notice");
|
|
1212
1222
|
|
|
1223
|
+
sidebarTasks.delete(id);
|
|
1224
|
+
delete taskTreeOpen[id];
|
|
1225
|
+
saveTaskTreeOpen();
|
|
1213
1226
|
sessionRows = sessionRows.filter((s) => s.id !== id);
|
|
1214
1227
|
paintSessions();
|
|
1215
1228
|
await loadSessions();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/page/client/state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/page/client/state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2gD9B,CAAC"}
|