privateboard 0.1.8 → 0.1.9
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/cli.js +879 -180
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/public/adjourn-overlay.css +2 -1
- package/public/agent-profile.css +525 -0
- package/public/agent-profile.js +278 -1
- package/public/app.js +269 -113
- package/public/index.html +304 -126
- package/public/magazine.html +1257 -838
- package/public/newspaper.html +1389 -1267
- package/public/ppt.html +2623 -0
- package/public/room-settings.css +40 -4
- package/public/room-settings.js +44 -2
- package/public/user-settings.css +23 -21
- package/public/user-settings.js +1 -4
- package/public/bento.html +0 -1396
package/public/agent-profile.js
CHANGED
|
@@ -1198,6 +1198,68 @@
|
|
|
1198
1198
|
}
|
|
1199
1199
|
return r.json();
|
|
1200
1200
|
}
|
|
1201
|
+
/** Spawn the dream-cycle modal overlay · returns the overlay
|
|
1202
|
+
* element so the caller can mount its body content. Idempotent
|
|
1203
|
+
* on re-entry (the user double-clicked) — closes any prior
|
|
1204
|
+
* overlay first so we never stack them. ESC + backdrop click
|
|
1205
|
+
* + ✕ button all funnel into closeDreamOverlay. */
|
|
1206
|
+
function openDreamOverlay() {
|
|
1207
|
+
closeDreamOverlay();
|
|
1208
|
+
const overlay = document.createElement("div");
|
|
1209
|
+
overlay.className = "dream-overlay";
|
|
1210
|
+
overlay.setAttribute("role", "dialog");
|
|
1211
|
+
overlay.setAttribute("aria-modal", "true");
|
|
1212
|
+
overlay.dataset.dreamOverlay = "1";
|
|
1213
|
+
overlay.innerHTML = `
|
|
1214
|
+
<div class="dream-backdrop" data-dream-close></div>
|
|
1215
|
+
<div class="dream-modal" role="document">
|
|
1216
|
+
<div class="dream-modal-head">
|
|
1217
|
+
<span class="dream-modal-kicker"><span class="dream-modal-glyph">☾</span> memory · sleep mode</span>
|
|
1218
|
+
<button type="button" class="dream-modal-close" data-dream-close aria-label="Close">✕</button>
|
|
1219
|
+
</div>
|
|
1220
|
+
<div class="dream-modal-body" data-dream-body></div>
|
|
1221
|
+
</div>
|
|
1222
|
+
`;
|
|
1223
|
+
document.body.appendChild(overlay);
|
|
1224
|
+
document.body.style.overflow = "hidden";
|
|
1225
|
+
// Backdrop / ✕ click → close. Listener bound on the overlay
|
|
1226
|
+
// root, delegated to anything carrying [data-dream-close].
|
|
1227
|
+
overlay.addEventListener("click", (ev) => {
|
|
1228
|
+
if (ev.target.closest && ev.target.closest("[data-dream-close]")) {
|
|
1229
|
+
ev.preventDefault();
|
|
1230
|
+
closeDreamOverlay();
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
// ESC closes too. Detached on close so we don't accumulate
|
|
1234
|
+
// listeners across opens.
|
|
1235
|
+
overlay.__dreamEsc = (ev) => {
|
|
1236
|
+
if (ev.key === "Escape") {
|
|
1237
|
+
ev.stopImmediatePropagation();
|
|
1238
|
+
closeDreamOverlay();
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
document.addEventListener("keydown", overlay.__dreamEsc, true);
|
|
1242
|
+
return overlay;
|
|
1243
|
+
}
|
|
1244
|
+
function closeDreamOverlay() {
|
|
1245
|
+
const overlay = document.querySelector('[data-dream-overlay="1"]');
|
|
1246
|
+
if (!overlay) return;
|
|
1247
|
+
if (overlay.__dreamPhaseTimer) {
|
|
1248
|
+
clearInterval(overlay.__dreamPhaseTimer);
|
|
1249
|
+
overlay.__dreamPhaseTimer = null;
|
|
1250
|
+
}
|
|
1251
|
+
if (overlay.__dreamAutoClose) {
|
|
1252
|
+
clearTimeout(overlay.__dreamAutoClose);
|
|
1253
|
+
overlay.__dreamAutoClose = null;
|
|
1254
|
+
}
|
|
1255
|
+
if (overlay.__dreamEsc) {
|
|
1256
|
+
document.removeEventListener("keydown", overlay.__dreamEsc, true);
|
|
1257
|
+
overlay.__dreamEsc = null;
|
|
1258
|
+
}
|
|
1259
|
+
overlay.remove();
|
|
1260
|
+
document.body.style.overflow = "";
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1201
1263
|
/** DELETE /api/agents/:slug/memories/:id */
|
|
1202
1264
|
async function deleteMemoryFor(slug, id) {
|
|
1203
1265
|
const r = await fetch(
|
|
@@ -2300,7 +2362,10 @@
|
|
|
2300
2362
|
<section class="ap-block">
|
|
2301
2363
|
<header class="ap-block-h">
|
|
2302
2364
|
<span class="ap-block-h-title">Memory</span>
|
|
2303
|
-
<
|
|
2365
|
+
<div class="ap-block-h-actions">
|
|
2366
|
+
<button type="button" class="ap-block-h-action ap-dream-trigger" data-ap-dream-trigger data-slug="${escape(slug)}" title="Consolidate · drop stale notes, keep stable patterns">☾ run consolidation</button>
|
|
2367
|
+
<button type="button" class="ap-block-h-action" data-ap-memory-add-toggle data-slug="${escape(slug)}">+ add note</button>
|
|
2368
|
+
</div>
|
|
2304
2369
|
</header>
|
|
2305
2370
|
${renderMemoryBlock(slug)}
|
|
2306
2371
|
</section>
|
|
@@ -2626,6 +2691,14 @@
|
|
|
2626
2691
|
document.addEventListener("click", (e) => {
|
|
2627
2692
|
const trigger = e.target.closest("[data-agent-profile]");
|
|
2628
2693
|
if (!trigger) return;
|
|
2694
|
+
// Per-row action buttons (pin toggle, future delete / overflow
|
|
2695
|
+
// glyphs that live INSIDE the agent-row anchor) need their own
|
|
2696
|
+
// click to land. Without this guard, the capture-phase
|
|
2697
|
+
// stopPropagation below swallows the action's bubble path and
|
|
2698
|
+
// opens the profile instead — making "click pin → nothing
|
|
2699
|
+
// happens" the visible bug. Bail out here so the action's own
|
|
2700
|
+
// delegated handler runs as intended.
|
|
2701
|
+
if (e.target.closest("[data-pin-toggle], [data-row-action]")) return;
|
|
2629
2702
|
const slug = trigger.dataset.agentProfile;
|
|
2630
2703
|
if (!slug) return;
|
|
2631
2704
|
// Accept either a hardcoded profile or any live agent the app
|
|
@@ -2854,6 +2927,210 @@
|
|
|
2854
2927
|
.catch((err) => alert("Couldn't delete: " + (err && err.message ? err.message : err)));
|
|
2855
2928
|
return;
|
|
2856
2929
|
}
|
|
2930
|
+
// Memory · manual consolidation trigger (chair "⊘ run consolidation"
|
|
2931
|
+
// button). Hits POST /api/agents/:id/dream, shows the before/after
|
|
2932
|
+
// counts inline for a few seconds, refreshes the memory list. The
|
|
2933
|
+
// button is disabled while the cycle runs to prevent double-click.
|
|
2934
|
+
const dreamTrigger = e.target.closest("[data-ap-dream-trigger]");
|
|
2935
|
+
if (dreamTrigger) {
|
|
2936
|
+
e.preventDefault();
|
|
2937
|
+
const slug = dreamTrigger.getAttribute("data-slug");
|
|
2938
|
+
if (!slug || dreamTrigger.disabled) return;
|
|
2939
|
+
const origLabel = dreamTrigger.textContent;
|
|
2940
|
+
dreamTrigger.disabled = true;
|
|
2941
|
+
dreamTrigger.textContent = "consolidating…";
|
|
2942
|
+
// Spawn the overlay · running widget mounted inside.
|
|
2943
|
+
// Earlier rev rendered the widget inline above the memory
|
|
2944
|
+
// list which felt cramped and shifted the page layout.
|
|
2945
|
+
// The overlay treatment matches the adjourn / supplement
|
|
2946
|
+
// modals' chrome so the dream reads as a proper "agent is
|
|
2947
|
+
// sleeping now" event with focused attention.
|
|
2948
|
+
const overlay = openDreamOverlay();
|
|
2949
|
+
const body = overlay.querySelector("[data-dream-body]");
|
|
2950
|
+
body.innerHTML = `
|
|
2951
|
+
<div class="dream-stage-pad">
|
|
2952
|
+
<div class="dream-section-kicker">
|
|
2953
|
+
<span class="dream-kicker-text">// running</span>
|
|
2954
|
+
<span class="dream-step" data-dream-step>1 / 6</span>
|
|
2955
|
+
</div>
|
|
2956
|
+
<div class="dream-frame" data-dream-state="running">
|
|
2957
|
+
<div class="dream-sky" aria-hidden="true">
|
|
2958
|
+
<span class="dream-z z1">Z</span>
|
|
2959
|
+
<span class="dream-z z2">z</span>
|
|
2960
|
+
<span class="dream-z z3">z</span>
|
|
2961
|
+
<span class="dream-z z4">Z</span>
|
|
2962
|
+
<span class="dream-z z5">z</span>
|
|
2963
|
+
</div>
|
|
2964
|
+
<div class="dream-lanes" aria-hidden="true">
|
|
2965
|
+
<div class="dream-lane lane-decay">
|
|
2966
|
+
<span class="dream-lane-label">decay</span>
|
|
2967
|
+
<span class="dream-lane-bar"><i></i></span>
|
|
2968
|
+
</div>
|
|
2969
|
+
<div class="dream-lane lane-merge">
|
|
2970
|
+
<span class="dream-lane-label">merge</span>
|
|
2971
|
+
<span class="dream-lane-bar"><i></i></span>
|
|
2972
|
+
</div>
|
|
2973
|
+
<div class="dream-lane lane-promote">
|
|
2974
|
+
<span class="dream-lane-label">promote</span>
|
|
2975
|
+
<span class="dream-lane-bar"><i></i></span>
|
|
2976
|
+
</div>
|
|
2977
|
+
</div>
|
|
2978
|
+
</div>
|
|
2979
|
+
<div class="dream-divider" aria-hidden="true"></div>
|
|
2980
|
+
<div class="dream-caption">
|
|
2981
|
+
<span class="dream-phase" data-dream-phase>scanning recent extractions…</span>
|
|
2982
|
+
</div>
|
|
2983
|
+
</div>
|
|
2984
|
+
`;
|
|
2985
|
+
// Rotate phase text every 1.6s · matches the actual
|
|
2986
|
+
// pipeline stages so the user's mental model of "what's
|
|
2987
|
+
// happening" tracks the backend.
|
|
2988
|
+
const phases = [
|
|
2989
|
+
"scanning recent extractions…",
|
|
2990
|
+
"clustering near-duplicates…",
|
|
2991
|
+
"merging clusters into canonical notes…",
|
|
2992
|
+
"resolving contradictions…",
|
|
2993
|
+
"promoting stable cross-room patterns…",
|
|
2994
|
+
"settling memory…",
|
|
2995
|
+
];
|
|
2996
|
+
let phaseIdx = 0;
|
|
2997
|
+
const phaseTimer = setInterval(() => {
|
|
2998
|
+
phaseIdx = (phaseIdx + 1) % phases.length;
|
|
2999
|
+
const phaseEl = overlay.querySelector("[data-dream-phase]");
|
|
3000
|
+
const stepEl = overlay.querySelector("[data-dream-step]");
|
|
3001
|
+
if (phaseEl) phaseEl.textContent = phases[phaseIdx];
|
|
3002
|
+
if (stepEl) stepEl.textContent = `${phaseIdx + 1} / ${phases.length}`;
|
|
3003
|
+
}, 1600);
|
|
3004
|
+
// Pin the timer on the overlay so closeDreamOverlay can
|
|
3005
|
+
// also clear it (user can dismiss mid-run via ✕ / ESC /
|
|
3006
|
+
// backdrop and we shouldn't keep the interval ticking).
|
|
3007
|
+
overlay.__dreamPhaseTimer = phaseTimer;
|
|
3008
|
+
fetch("/api/agents/" + encodeURIComponent(slug) + "/dream", {
|
|
3009
|
+
method: "POST",
|
|
3010
|
+
headers: { "content-type": "application/json" },
|
|
3011
|
+
body: JSON.stringify({}),
|
|
3012
|
+
})
|
|
3013
|
+
.then(async (r) => {
|
|
3014
|
+
const j = await r.json().catch(() => ({}));
|
|
3015
|
+
if (!r.ok) throw new Error(j.error || ("HTTP " + r.status));
|
|
3016
|
+
return j;
|
|
3017
|
+
})
|
|
3018
|
+
.then((j) => {
|
|
3019
|
+
const s = j.summary || {};
|
|
3020
|
+
const before = s.beforeCount ?? 0;
|
|
3021
|
+
const after = s.afterCount ?? 0;
|
|
3022
|
+
const decayed = s.decayed ?? 0;
|
|
3023
|
+
const merged = s.merged ?? 0;
|
|
3024
|
+
const promoted = s.promoted ?? 0;
|
|
3025
|
+
const superseded = s.superseded ?? 0;
|
|
3026
|
+
// Stop phase rotation now that we're swapping to result.
|
|
3027
|
+
if (overlay.__dreamPhaseTimer) {
|
|
3028
|
+
clearInterval(overlay.__dreamPhaseTimer);
|
|
3029
|
+
overlay.__dreamPhaseTimer = null;
|
|
3030
|
+
}
|
|
3031
|
+
// Flip the modal class so the chrome (border / kicker
|
|
3032
|
+
// tint) follows the resolved state, then swap body.
|
|
3033
|
+
overlay.classList.add("is-done");
|
|
3034
|
+
const allZero = decayed === 0 && merged === 0 && promoted === 0 && superseded === 0;
|
|
3035
|
+
const delta = before - after;
|
|
3036
|
+
if (allZero) {
|
|
3037
|
+
// No-op result · still uses the full stage layout so
|
|
3038
|
+
// the modal doesn't visually shrink between phases.
|
|
3039
|
+
// Hero is the moon glyph + "settled" headline; the
|
|
3040
|
+
// bottom half is a quiet caption acknowledging the
|
|
3041
|
+
// already-tidy state.
|
|
3042
|
+
body.innerHTML = `
|
|
3043
|
+
<div class="dream-stage-pad is-quiet">
|
|
3044
|
+
<div class="dream-section-kicker">
|
|
3045
|
+
<span class="dream-kicker-text">// settled</span>
|
|
3046
|
+
<span class="dream-step is-quiet">no change</span>
|
|
3047
|
+
</div>
|
|
3048
|
+
<div class="dream-hero is-quiet">
|
|
3049
|
+
<span class="dream-hero-glyph">☾</span>
|
|
3050
|
+
<div class="dream-hero-text">
|
|
3051
|
+
<div class="dream-hero-title">Memory is already settled</div>
|
|
3052
|
+
<div class="dream-hero-sub">${after} note${after === 1 ? "" : "s"} · nothing to consolidate</div>
|
|
3053
|
+
</div>
|
|
3054
|
+
</div>
|
|
3055
|
+
<div class="dream-caption">
|
|
3056
|
+
<span class="dream-caption-foot">no duplicates · no contradictions · pile is clean.</span>
|
|
3057
|
+
</div>
|
|
3058
|
+
</div>
|
|
3059
|
+
`;
|
|
3060
|
+
} else {
|
|
3061
|
+
// Non-trivial result · hero shows the count delta
|
|
3062
|
+
// (was → now framed) with a magnitude chip; tiles
|
|
3063
|
+
// below break the change down per operation.
|
|
3064
|
+
const tile = (n, label, cls) =>
|
|
3065
|
+
`<div class="dream-tile ${cls}${n === 0 ? " is-empty" : ""}">
|
|
3066
|
+
<span class="dream-tile-n">${n}</span>
|
|
3067
|
+
<span class="dream-tile-l">${label}</span>
|
|
3068
|
+
</div>`;
|
|
3069
|
+
const deltaLabel = delta > 0 ? `−${delta}` : delta < 0 ? `+${Math.abs(delta)}` : "±0";
|
|
3070
|
+
const deltaTone = delta > 0 ? "is-shrink" : delta < 0 ? "is-grow" : "is-flat";
|
|
3071
|
+
body.innerHTML = `
|
|
3072
|
+
<div class="dream-stage-pad">
|
|
3073
|
+
<div class="dream-section-kicker">
|
|
3074
|
+
<span class="dream-kicker-text">// consolidated</span>
|
|
3075
|
+
<span class="dream-step ${deltaTone}">${deltaLabel}</span>
|
|
3076
|
+
</div>
|
|
3077
|
+
<div class="dream-hero">
|
|
3078
|
+
<div class="dream-hero-numerals">
|
|
3079
|
+
<span class="dream-num was">${before}</span>
|
|
3080
|
+
<span class="dream-num-arrow" aria-hidden="true">→</span>
|
|
3081
|
+
<span class="dream-num-frame">
|
|
3082
|
+
<span class="dream-num now">${after}</span>
|
|
3083
|
+
</span>
|
|
3084
|
+
</div>
|
|
3085
|
+
<span class="dream-hero-unit">note${after === 1 ? "" : "s"}</span>
|
|
3086
|
+
</div>
|
|
3087
|
+
<div class="dream-divider" aria-hidden="true"></div>
|
|
3088
|
+
<div class="dream-tiles">
|
|
3089
|
+
${tile(decayed, "decayed", "is-decay")}
|
|
3090
|
+
${tile(merged, "merged", "is-merge")}
|
|
3091
|
+
${tile(superseded, "superseded", "is-supersede")}
|
|
3092
|
+
${tile(promoted, "promoted", "is-promote")}
|
|
3093
|
+
</div>
|
|
3094
|
+
</div>
|
|
3095
|
+
`;
|
|
3096
|
+
}
|
|
3097
|
+
// Auto-dismiss after a longer beat so the user has
|
|
3098
|
+
// time to read the tiles. Manual dismissal still
|
|
3099
|
+
// works via ✕ / ESC / backdrop.
|
|
3100
|
+
overlay.__dreamAutoClose = setTimeout(() => closeDreamOverlay(), 9000);
|
|
3101
|
+
return loadMemoriesFor(slug);
|
|
3102
|
+
})
|
|
3103
|
+
.catch((err) => {
|
|
3104
|
+
if (overlay.__dreamPhaseTimer) {
|
|
3105
|
+
clearInterval(overlay.__dreamPhaseTimer);
|
|
3106
|
+
overlay.__dreamPhaseTimer = null;
|
|
3107
|
+
}
|
|
3108
|
+
overlay.classList.add("is-error");
|
|
3109
|
+
body.innerHTML = `
|
|
3110
|
+
<div class="dream-stage-pad is-quiet">
|
|
3111
|
+
<div class="dream-section-kicker">
|
|
3112
|
+
<span class="dream-kicker-text">// failed</span>
|
|
3113
|
+
<span class="dream-step is-error">error</span>
|
|
3114
|
+
</div>
|
|
3115
|
+
<div class="dream-hero is-quiet">
|
|
3116
|
+
<span class="dream-hero-glyph">✕</span>
|
|
3117
|
+
<div class="dream-hero-text">
|
|
3118
|
+
<div class="dream-hero-title">Consolidation failed</div>
|
|
3119
|
+
<div class="dream-hero-sub">${escape(err && err.message ? err.message : String(err))}</div>
|
|
3120
|
+
</div>
|
|
3121
|
+
</div>
|
|
3122
|
+
<div class="dream-caption">
|
|
3123
|
+
<span class="dream-caption-foot">memory pile unchanged · safe to retry.</span>
|
|
3124
|
+
</div>
|
|
3125
|
+
</div>
|
|
3126
|
+
`;
|
|
3127
|
+
})
|
|
3128
|
+
.finally(() => {
|
|
3129
|
+
dreamTrigger.disabled = false;
|
|
3130
|
+
dreamTrigger.textContent = origLabel;
|
|
3131
|
+
});
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
2857
3134
|
// Memory · toggle the add form (mirrors the Rules add pattern · the
|
|
2858
3135
|
// input area is hidden by default and only revealed when the user
|
|
2859
3136
|
// clicks the [+ add note] section action).
|