@symerian/symi 3.5.14 → 3.5.15
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/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
542830bc5960faae2edcb1d271ead8ae8b0e4a4a5f2cfbcaadd26c9aa81b7c22
|
|
@@ -907,6 +907,86 @@ body {
|
|
|
907
907
|
border-color: rgba(0, 212, 255, 0.18);
|
|
908
908
|
}
|
|
909
909
|
|
|
910
|
+
/* Phase 2: dynamic-card sub-elements (only present on cards added via
|
|
911
|
+
symframe.add(); the 3 sticky panels render their own internals). */
|
|
912
|
+
.symframe-card-header {
|
|
913
|
+
display: flex;
|
|
914
|
+
align-items: center;
|
|
915
|
+
justify-content: space-between;
|
|
916
|
+
gap: 8px;
|
|
917
|
+
}
|
|
918
|
+
.symframe-card-dismiss {
|
|
919
|
+
background: transparent;
|
|
920
|
+
border: none;
|
|
921
|
+
color: var(--text-dim);
|
|
922
|
+
font-size: 16px;
|
|
923
|
+
line-height: 1;
|
|
924
|
+
width: 18px;
|
|
925
|
+
height: 18px;
|
|
926
|
+
border-radius: 4px;
|
|
927
|
+
cursor: pointer;
|
|
928
|
+
opacity: 0.6;
|
|
929
|
+
transition:
|
|
930
|
+
opacity 0.15s ease,
|
|
931
|
+
color 0.15s ease,
|
|
932
|
+
background 0.15s ease;
|
|
933
|
+
padding: 0;
|
|
934
|
+
display: flex;
|
|
935
|
+
align-items: center;
|
|
936
|
+
justify-content: center;
|
|
937
|
+
}
|
|
938
|
+
.symframe-card-dismiss:hover {
|
|
939
|
+
opacity: 1;
|
|
940
|
+
color: var(--accent-cyan);
|
|
941
|
+
background: rgba(0, 212, 255, 0.08);
|
|
942
|
+
}
|
|
943
|
+
.symframe-card-body {
|
|
944
|
+
font-family: var(--font-mono);
|
|
945
|
+
font-size: 11px;
|
|
946
|
+
line-height: 1.5;
|
|
947
|
+
color: var(--text);
|
|
948
|
+
margin-top: 8px;
|
|
949
|
+
white-space: pre-wrap;
|
|
950
|
+
word-break: break-word;
|
|
951
|
+
max-height: 320px;
|
|
952
|
+
overflow-y: auto;
|
|
953
|
+
}
|
|
954
|
+
.symframe-card-actions {
|
|
955
|
+
display: flex;
|
|
956
|
+
gap: 6px;
|
|
957
|
+
margin-top: 10px;
|
|
958
|
+
padding-top: 8px;
|
|
959
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
960
|
+
flex-wrap: wrap;
|
|
961
|
+
}
|
|
962
|
+
.symframe-card-action {
|
|
963
|
+
flex: 1 1 auto;
|
|
964
|
+
min-width: 60px;
|
|
965
|
+
padding: 6px 10px;
|
|
966
|
+
border: 1px solid var(--glass-border);
|
|
967
|
+
border-radius: 5px;
|
|
968
|
+
background: rgba(255, 255, 255, 0.03);
|
|
969
|
+
color: var(--text-dim);
|
|
970
|
+
font-family: var(--font-mono);
|
|
971
|
+
font-size: 10px;
|
|
972
|
+
letter-spacing: 0.04em;
|
|
973
|
+
cursor: pointer;
|
|
974
|
+
transition: all 0.15s ease;
|
|
975
|
+
}
|
|
976
|
+
.symframe-card-action:hover {
|
|
977
|
+
border-color: var(--accent-cyan);
|
|
978
|
+
color: var(--text);
|
|
979
|
+
}
|
|
980
|
+
.symframe-card-action-primary {
|
|
981
|
+
background: rgba(0, 212, 255, 0.08);
|
|
982
|
+
border-color: var(--accent-cyan);
|
|
983
|
+
color: var(--accent-cyan);
|
|
984
|
+
}
|
|
985
|
+
.symframe-card-action-primary:hover {
|
|
986
|
+
background: rgba(0, 212, 255, 0.14);
|
|
987
|
+
box-shadow: 0 0 8px rgba(0, 212, 255, 0.18);
|
|
988
|
+
}
|
|
989
|
+
|
|
910
990
|
/* ── Response Area ────────────────────────────────────────────────── */
|
|
911
991
|
.response-area {
|
|
912
992
|
position: fixed;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
// ── Symframe registry ─────────────────────────────────────────────────
|
|
2
|
+
// Phase 2 of the symframe arc. Provides `window.symframe.add/update/remove/list`
|
|
3
|
+
// for dynamic cards in the right column. The 3 existing panels (SUBAGENTS,
|
|
4
|
+
// REASONING, SCHEDULING) auto-register at boot as sticky cards — they keep
|
|
5
|
+
// their own DOM and JS hookups, the registry just tracks metadata.
|
|
6
|
+
//
|
|
7
|
+
// Card stack order: ephemeral (non-sticky) cards stack at the TOP of the
|
|
8
|
+
// container, newest at the very top; sticky cards anchor below them in
|
|
9
|
+
// fixed insertion order so the always-visible system panels never drift.
|
|
10
|
+
//
|
|
11
|
+
// Phase 3+ will add type-specific renderers + a server-side push RPC. For
|
|
12
|
+
// now the registry is JS-only and exercisable from the browser console:
|
|
13
|
+
// symframe.add({ title: "Test", body: "hello", dismissable: true });
|
|
14
|
+
// symframe.update("<id>", { body: "updated" });
|
|
15
|
+
// symframe.remove("<id>");
|
|
16
|
+
|
|
17
|
+
(function () {
|
|
18
|
+
const container = document.getElementById("symframe-cards");
|
|
19
|
+
const countEl = document.getElementById("symframe-count");
|
|
20
|
+
if (!container) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** @type {Map<string, Card>} */
|
|
25
|
+
const cards = new Map();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} Card
|
|
29
|
+
* @property {string} id
|
|
30
|
+
* @property {string} [type] generic descriptor; renderers in Phase 3+ key off this
|
|
31
|
+
* @property {string} title header text (rendered uppercase via existing .panel-label)
|
|
32
|
+
* @property {string} [body] plain-text body (escaped)
|
|
33
|
+
* @property {string} [bodyHtml] trusted-HTML body (no escape) — only set internally
|
|
34
|
+
* @property {Array<Action>} [actions] footer buttons
|
|
35
|
+
* @property {boolean} [sticky] true → anchored at bottom of stack, no dismiss control
|
|
36
|
+
* @property {boolean} [dismissable] true → header gains a × button that calls remove(id)
|
|
37
|
+
* @property {Element} [element] DOM element for register()-tracked cards (existing panels)
|
|
38
|
+
* @property {number} createdAt set by the registry
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @typedef {Object} Action
|
|
43
|
+
* @property {string} label
|
|
44
|
+
* @property {"primary"|"secondary"} [kind]
|
|
45
|
+
* @property {() => void} onClick
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
function genId() {
|
|
49
|
+
return `sf-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function updateCount() {
|
|
53
|
+
if (!countEl) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const total = cards.size;
|
|
57
|
+
countEl.textContent = total === 1 ? "1 card" : `${total} cards`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function renderCardElement(card) {
|
|
61
|
+
const el = document.createElement("div");
|
|
62
|
+
el.className = "glass-panel symframe-card symframe-card-dynamic";
|
|
63
|
+
if (card.sticky) {
|
|
64
|
+
el.classList.add("symframe-card-sticky");
|
|
65
|
+
}
|
|
66
|
+
el.dataset.cardId = card.id;
|
|
67
|
+
if (card.type) {
|
|
68
|
+
el.dataset.cardType = card.type;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Header
|
|
72
|
+
const header = document.createElement("div");
|
|
73
|
+
header.className = "panel-label symframe-card-header";
|
|
74
|
+
const titleSpan = document.createElement("span");
|
|
75
|
+
titleSpan.textContent = card.title || "";
|
|
76
|
+
header.appendChild(titleSpan);
|
|
77
|
+
if (card.dismissable && !card.sticky) {
|
|
78
|
+
const dismiss = document.createElement("button");
|
|
79
|
+
dismiss.className = "symframe-card-dismiss";
|
|
80
|
+
dismiss.type = "button";
|
|
81
|
+
dismiss.setAttribute("aria-label", "Dismiss");
|
|
82
|
+
dismiss.textContent = "×";
|
|
83
|
+
dismiss.addEventListener("click", (e) => {
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
e.stopPropagation();
|
|
86
|
+
remove(card.id);
|
|
87
|
+
});
|
|
88
|
+
header.appendChild(dismiss);
|
|
89
|
+
}
|
|
90
|
+
el.appendChild(header);
|
|
91
|
+
|
|
92
|
+
// Body
|
|
93
|
+
if (card.bodyHtml || card.body) {
|
|
94
|
+
const body = document.createElement("div");
|
|
95
|
+
body.className = "symframe-card-body";
|
|
96
|
+
if (card.bodyHtml) {
|
|
97
|
+
body.innerHTML = card.bodyHtml;
|
|
98
|
+
} else {
|
|
99
|
+
body.textContent = card.body;
|
|
100
|
+
}
|
|
101
|
+
el.appendChild(body);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Actions
|
|
105
|
+
if (Array.isArray(card.actions) && card.actions.length > 0) {
|
|
106
|
+
const actionsRow = document.createElement("div");
|
|
107
|
+
actionsRow.className = "symframe-card-actions";
|
|
108
|
+
for (const action of card.actions) {
|
|
109
|
+
const btn = document.createElement("button");
|
|
110
|
+
btn.className = "symframe-card-action";
|
|
111
|
+
if (action.kind === "primary") {
|
|
112
|
+
btn.classList.add("symframe-card-action-primary");
|
|
113
|
+
}
|
|
114
|
+
btn.type = "button";
|
|
115
|
+
btn.textContent = action.label || "";
|
|
116
|
+
if (typeof action.onClick === "function") {
|
|
117
|
+
btn.addEventListener("click", (e) => {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
try {
|
|
120
|
+
action.onClick();
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.warn("[symframe] action threw:", err);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
actionsRow.appendChild(btn);
|
|
127
|
+
}
|
|
128
|
+
el.appendChild(actionsRow);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return el;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function insertElement(card, el) {
|
|
135
|
+
if (card.sticky) {
|
|
136
|
+
// Sticky cards anchor below all ephemeral cards. Append to end of
|
|
137
|
+
// the sticky group (which is the end of the container, since
|
|
138
|
+
// ephemeral cards live above).
|
|
139
|
+
container.appendChild(el);
|
|
140
|
+
} else {
|
|
141
|
+
// Ephemeral cards stack at the top, newest first → prepend.
|
|
142
|
+
container.prepend(el);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Track an existing DOM-managed card (e.g. SUBAGENTS panel). The
|
|
148
|
+
* element is already in the container and renders/updates itself; the
|
|
149
|
+
* registry just records metadata so list/count/sort logic can see it.
|
|
150
|
+
*/
|
|
151
|
+
function register(card) {
|
|
152
|
+
if (!card || !card.id || !card.element) {
|
|
153
|
+
console.warn("[symframe] register: missing id or element");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const entry = {
|
|
157
|
+
...card,
|
|
158
|
+
sticky: card.sticky !== false,
|
|
159
|
+
dismissable: false,
|
|
160
|
+
createdAt: Date.now(),
|
|
161
|
+
};
|
|
162
|
+
cards.set(entry.id, entry);
|
|
163
|
+
entry.element.classList.add("symframe-card");
|
|
164
|
+
if (entry.sticky) {
|
|
165
|
+
entry.element.classList.add("symframe-card-sticky");
|
|
166
|
+
}
|
|
167
|
+
updateCount();
|
|
168
|
+
return entry;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Add a new dynamic card. Renders into the container, prepended for
|
|
173
|
+
* ephemeral cards or appended for sticky cards.
|
|
174
|
+
*/
|
|
175
|
+
function add(card) {
|
|
176
|
+
const id = card?.id || genId();
|
|
177
|
+
if (cards.has(id)) {
|
|
178
|
+
// Treat as update if id collides.
|
|
179
|
+
return update(id, card);
|
|
180
|
+
}
|
|
181
|
+
const entry = {
|
|
182
|
+
...card,
|
|
183
|
+
id,
|
|
184
|
+
sticky: card?.sticky === true,
|
|
185
|
+
dismissable: card?.dismissable !== false,
|
|
186
|
+
createdAt: Date.now(),
|
|
187
|
+
};
|
|
188
|
+
const el = renderCardElement(entry);
|
|
189
|
+
entry.element = el;
|
|
190
|
+
cards.set(id, entry);
|
|
191
|
+
insertElement(entry, el);
|
|
192
|
+
updateCount();
|
|
193
|
+
return entry;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Patch an existing card. Re-renders the card's DOM element from the
|
|
198
|
+
* merged data; preserves stack position.
|
|
199
|
+
*/
|
|
200
|
+
function update(id, patch) {
|
|
201
|
+
const existing = cards.get(id);
|
|
202
|
+
if (!existing) {
|
|
203
|
+
console.warn(`[symframe] update: unknown id ${id}`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (!existing.element) {
|
|
207
|
+
console.warn(`[symframe] update: card ${id} has no element (registered, not added)`);
|
|
208
|
+
// Allow metadata patch without re-render
|
|
209
|
+
const merged = { ...existing, ...patch, id };
|
|
210
|
+
cards.set(id, merged);
|
|
211
|
+
return merged;
|
|
212
|
+
}
|
|
213
|
+
const merged = { ...existing, ...patch, id };
|
|
214
|
+
const newEl = renderCardElement(merged);
|
|
215
|
+
merged.element = newEl;
|
|
216
|
+
existing.element.replaceWith(newEl);
|
|
217
|
+
cards.set(id, merged);
|
|
218
|
+
return merged;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Remove a card from the registry + DOM. Sticky cards refuse removal
|
|
223
|
+
* unless force=true is passed (defensive — the system panels really
|
|
224
|
+
* shouldn't disappear via stray API calls).
|
|
225
|
+
*/
|
|
226
|
+
function remove(id, force = false) {
|
|
227
|
+
const entry = cards.get(id);
|
|
228
|
+
if (!entry) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
if (entry.sticky && !force) {
|
|
232
|
+
console.warn(`[symframe] remove: refusing to drop sticky card '${id}' without force=true`);
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
if (entry.element && entry.element.parentElement === container) {
|
|
236
|
+
entry.element.remove();
|
|
237
|
+
}
|
|
238
|
+
cards.delete(id);
|
|
239
|
+
updateCount();
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Snapshot of all registered cards. */
|
|
244
|
+
function list() {
|
|
245
|
+
return Array.from(cards.values());
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Auto-register the 3 sticky panels that already live in the DOM. The
|
|
249
|
+
// existing JS for each panel (subagents.js, scheduling.js, reasoning
|
|
250
|
+
// render code) keeps targeting them by id and is unaffected.
|
|
251
|
+
const KNOWN_STICKY = [
|
|
252
|
+
{ id: "subagents", elementId: "subagents-panel", title: "SUBAGENTS", type: "panel-subagents" },
|
|
253
|
+
{ id: "reasoning", elementId: "reasoning-panel", title: "REASONING", type: "panel-reasoning" },
|
|
254
|
+
{
|
|
255
|
+
id: "scheduling",
|
|
256
|
+
elementId: "scheduling-panel",
|
|
257
|
+
title: "SCHEDULING",
|
|
258
|
+
type: "panel-scheduling",
|
|
259
|
+
},
|
|
260
|
+
];
|
|
261
|
+
for (const known of KNOWN_STICKY) {
|
|
262
|
+
const el = document.getElementById(known.elementId);
|
|
263
|
+
if (el) {
|
|
264
|
+
register({
|
|
265
|
+
id: known.id,
|
|
266
|
+
type: known.type,
|
|
267
|
+
title: known.title,
|
|
268
|
+
sticky: true,
|
|
269
|
+
element: el,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
window.symframe = { add, update, remove, list, register };
|
|
275
|
+
})();
|