viberoom 0.5.2 → 0.5.4
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/NOTICE +8 -2
- package/README.md +1 -1
- package/dist/hub.js +55 -2
- package/dist/main.js +10 -0
- package/dist/room.js +43 -3
- package/dist/server.js +41 -1
- package/dist/templates.js +27 -3
- package/dist/update.js +95 -0
- package/package.json +1 -1
- package/templates/wren-and-quinn/template.json +28 -0
- package/ui/app.css +61 -19
- package/ui/app.js +306 -14
- package/ui/fonts/fira-code-cyrillic-ext.woff2 +0 -0
- package/ui/fonts/fira-code-cyrillic.woff2 +0 -0
- package/ui/fonts/fira-code-latin-ext.woff2 +0 -0
- package/ui/fonts/fira-code-latin.woff2 +0 -0
- package/ui/fonts/fira-code.css +5 -0
- package/ui/fonts/inter-cyrillic-ext.woff2 +0 -0
- package/ui/fonts/inter-cyrillic.woff2 +0 -0
- package/ui/fonts/inter-latin-ext.woff2 +0 -0
- package/ui/fonts/inter-latin.woff2 +0 -0
- package/ui/fonts/inter.css +5 -0
- package/ui/fonts/jetbrains-mono-cyrillic-ext.woff2 +0 -0
- package/ui/fonts/jetbrains-mono-cyrillic.woff2 +0 -0
- package/ui/fonts/jetbrains-mono-latin-ext.woff2 +0 -0
- package/ui/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/ui/fonts/jetbrains-mono.css +5 -0
- package/ui/fonts/noto-sans-cyrillic-ext.woff2 +0 -0
- package/ui/fonts/noto-sans-cyrillic.woff2 +0 -0
- package/ui/fonts/noto-sans-latin-ext.woff2 +0 -0
- package/ui/fonts/noto-sans-latin.woff2 +0 -0
- package/ui/fonts/noto-sans.css +5 -0
- package/ui/fonts/source-code-pro-cyrillic-ext.woff2 +0 -0
- package/ui/fonts/source-code-pro-cyrillic.woff2 +0 -0
- package/ui/fonts/source-code-pro-latin-ext.woff2 +0 -0
- package/ui/fonts/source-code-pro-latin.woff2 +0 -0
- package/ui/fonts/source-code-pro.css +5 -0
- package/ui/index.html +11 -0
- package/ui/theme.css +44 -18
- package/templates/forge-and-lumen/template.json +0 -28
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/* viberoom - Copyright (c) 2026 Todor Rusev - AGPL-3.0-or-later; see LICENSE */
|
|
2
|
+
@font-face { font-family: "Source Code Pro"; font-style: normal; font-weight: 400 700; font-display: swap; src: url("/fonts/source-code-pro-cyrillic-ext.woff2") format("woff2"); unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
|
|
3
|
+
@font-face { font-family: "Source Code Pro"; font-style: normal; font-weight: 400 700; font-display: swap; src: url("/fonts/source-code-pro-cyrillic.woff2") format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
|
|
4
|
+
@font-face { font-family: "Source Code Pro"; font-style: normal; font-weight: 400 700; font-display: swap; src: url("/fonts/source-code-pro-latin-ext.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
|
|
5
|
+
@font-face { font-family: "Source Code Pro"; font-style: normal; font-weight: 400 700; font-display: swap; src: url("/fonts/source-code-pro-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
|
package/ui/index.html
CHANGED
|
@@ -164,6 +164,17 @@
|
|
|
164
164
|
<div class="actions"><button type="button" class="btn ghost" data-close>Cancel</button><button type="submit" class="btn primary" id="tpl-create">Create the room</button></div>
|
|
165
165
|
</form>
|
|
166
166
|
</dialog>
|
|
167
|
+
<dialog id="save-template-dialog" class="dialog wide">
|
|
168
|
+
<form id="save-template-form" method="dialog">
|
|
169
|
+
<h3><span class="h-ico" data-icon="rooms"></span>Turn this room into a template</h3>
|
|
170
|
+
<p class="lead">Everything below goes into the template: the room's settings and rules, its folder, and each vibemate with the coding agent it runs on. Check it, name it, create it.</p>
|
|
171
|
+
<label>Template name<input id="stp-name" type="text" maxlength="60" required placeholder="e.g. Payments refactor"></label>
|
|
172
|
+
<label>Description <span class="hint">(optional)</span><textarea id="stp-desc" rows="2" maxlength="400" placeholder="what this setup is good for"></textarea></label>
|
|
173
|
+
<div class="stp-preview" id="stp-preview"></div>
|
|
174
|
+
<p class="error" id="stp-error" hidden></p>
|
|
175
|
+
<div class="actions"><button type="button" class="btn ghost" data-close>Cancel</button><button type="submit" class="btn primary" id="stp-create">Create the template</button></div>
|
|
176
|
+
</form>
|
|
177
|
+
</dialog>
|
|
167
178
|
<dialog id="invite-dialog" class="dialog wide">
|
|
168
179
|
<form id="invite-form" method="dialog">
|
|
169
180
|
<h3><span class="h-ico" data-icon="spark"></span>Summon a Vibemate</h3>
|
package/ui/theme.css
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/* viberoom - Copyright (c) 2026 Todor Rusev - AGPL-3.0-or-later; see LICENSE */
|
|
2
2
|
@import url("/fonts/nunito.css");
|
|
3
|
+
@import url("/fonts/inter.css");
|
|
4
|
+
@import url("/fonts/noto-sans.css");
|
|
5
|
+
@import url("/fonts/jetbrains-mono.css");
|
|
6
|
+
@import url("/fonts/fira-code.css");
|
|
7
|
+
@import url("/fonts/source-code-pro.css");
|
|
3
8
|
:root {
|
|
4
9
|
--primary: #5b5bf0;
|
|
5
10
|
--primary-deep: #4f4fe0;
|
|
@@ -48,6 +53,25 @@
|
|
|
48
53
|
--mine-border: transparent;
|
|
49
54
|
--text: var(--ink);
|
|
50
55
|
--ok: var(--green);
|
|
56
|
+
--orchid: #f3e4fb;
|
|
57
|
+
--orchid-ink: #8a3fc2;
|
|
58
|
+
--st-ready: var(--mint); --st-ready-ink: var(--mint-ink); --st-ready-dot: var(--green);
|
|
59
|
+
--st-waiting: var(--sky); --st-waiting-ink: #2a6fbf; --st-waiting-dot: #7aa7ff;
|
|
60
|
+
--st-thinking: var(--warm); --st-thinking-ink: var(--warm-ink); --st-thinking-dot: var(--warn);
|
|
61
|
+
--st-writing: var(--orchid); --st-writing-ink: var(--orchid-ink); --st-writing-dot: #b06be0;
|
|
62
|
+
--st-error: var(--rose); --st-error-ink: var(--rose-ink); --st-error-dot: var(--rose-ink);
|
|
63
|
+
--st-asleep: var(--softer); --st-asleep-ink: var(--muted); --st-asleep-dot: var(--faint);
|
|
64
|
+
--attention: #ff8a3d;
|
|
65
|
+
--attention-ink: #c9461c;
|
|
66
|
+
--attention-grad: linear-gradient(135deg, #ff9447, #e8642a);
|
|
67
|
+
--attention-shadow: 0 8px 18px -10px rgba(232, 100, 42, 0.7);
|
|
68
|
+
--unread-grad: linear-gradient(135deg, #ff7a45, #f0452c);
|
|
69
|
+
--note-hover: #ffeaa8;
|
|
70
|
+
--tick-mine: #cdcdf9;
|
|
71
|
+
--tick-mine-view: #a9a9f5;
|
|
72
|
+
--tick-fallback: #9ca3af;
|
|
73
|
+
--unseen-ink: #be185d;
|
|
74
|
+
--unseen-bg: #fff1f7;
|
|
51
75
|
--r-xs: 8px;
|
|
52
76
|
--r-sm: 12px;
|
|
53
77
|
--r-md: 14px;
|
|
@@ -292,35 +316,37 @@ p .geek-tip, .hint > .geek-tip, .switch .label > .geek-tip { margin-left: 8px; }
|
|
|
292
316
|
.agent-tile.off { opacity: 0.5; filter: grayscale(1); cursor: default; }
|
|
293
317
|
.badge { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; padding: 3px 10px; border-radius: var(--r-pill); background: var(--lav); color: var(--primary); font-weight: 800; white-space: nowrap; vertical-align: middle; }
|
|
294
318
|
.badge.outline { background: transparent; border: 2px solid var(--lav-2); color: var(--muted); }
|
|
295
|
-
.badge.status-idle { background: var(--
|
|
296
|
-
.badge.status-thinking { background: var(--
|
|
297
|
-
.badge.status-
|
|
298
|
-
.badge.status-
|
|
299
|
-
.badge.status-
|
|
300
|
-
.badge.status-
|
|
301
|
-
.badge.status-
|
|
319
|
+
.badge.status-idle { background: var(--st-ready); color: var(--st-ready-ink); }
|
|
320
|
+
.badge.status-thinking { background: var(--st-thinking); color: var(--st-thinking-ink); }
|
|
321
|
+
.badge.status-writing { background: var(--st-writing); color: var(--st-writing-ink); }
|
|
322
|
+
.badge.status-queued { background: var(--st-waiting); color: var(--st-waiting-ink); }
|
|
323
|
+
.badge.status-starting { background: var(--st-waiting); color: var(--st-waiting-ink); }
|
|
324
|
+
.badge.status-offline { background: var(--st-asleep); color: var(--st-asleep-ink); }
|
|
325
|
+
.badge.status-error { background: var(--st-error); color: var(--st-error-ink); }
|
|
326
|
+
.badge.status-left { background: var(--st-asleep); color: var(--st-asleep-ink); }
|
|
302
327
|
.badge.muted { background: var(--softer); color: var(--muted); }
|
|
303
328
|
.badge .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
|
304
|
-
.badge.status-thinking .dot { animation: pulse 1.2s ease-in-out infinite; }
|
|
329
|
+
.badge.status-thinking .dot, .badge.status-writing .dot { animation: pulse 1.2s ease-in-out infinite; }
|
|
305
330
|
.chip { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 800; background: var(--lav); color: var(--primary); border: 0; border-radius: var(--r-pill); padding: 4px 10px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
306
331
|
.chip .i { width: 13px; height: 13px; }
|
|
307
|
-
.chip-completed { background: var(--
|
|
308
|
-
.chip-failed { background: var(--
|
|
309
|
-
.chip-in_progress { background: var(--
|
|
332
|
+
.chip-completed { background: var(--st-ready); color: var(--st-ready-ink); }
|
|
333
|
+
.chip-failed { background: var(--st-error); color: var(--st-error-ink); }
|
|
334
|
+
.chip-in_progress { background: var(--st-thinking); color: var(--st-thinking-ink); }
|
|
310
335
|
.count-pill { background: var(--primary); color: #fff; border-radius: var(--r-pill); font-size: 10px; padding: 1px 7px; font-weight: 800; line-height: 15px; }
|
|
311
336
|
.zzz { font-size: 10px; font-weight: 800; color: var(--muted); background: var(--softer); padding: 3px 8px; border-radius: var(--r-pill); }
|
|
312
|
-
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--
|
|
337
|
+
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--st-thinking-dot); box-shadow: 0 0 0 3px var(--st-thinking); animation: pulse 1.2s ease-in-out infinite; display: inline-block; }
|
|
313
338
|
.avatar { position: relative; display: inline-block; flex: none; }
|
|
314
339
|
.avatar .av-tile { display: grid; place-items: center; width: 100%; height: 100%; font-weight: 800; line-height: 1; font-family: var(--font); user-select: none; }
|
|
315
340
|
.avatar-badge { position: absolute; right: -4px; bottom: -4px; width: 44%; height: 44%; min-width: 15px; min-height: 15px; border-radius: 6px; background: #fff; border: 2px solid #fff; box-shadow: var(--shadow-tile); display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 800; color: var(--primary); overflow: hidden; }
|
|
316
341
|
.avatar-badge img { width: 72%; height: 72%; object-fit: contain; }
|
|
317
342
|
.avatar-status { position: absolute; left: -3px; bottom: -3px; width: 12px; height: 12px; border-radius: 50%; border: 2px solid #fff; background: var(--faint); }
|
|
318
|
-
.avatar-status.status-idle { background: var(--
|
|
319
|
-
.avatar-status.status-thinking { background: var(--
|
|
320
|
-
.avatar-status.status-
|
|
321
|
-
.avatar-status.status-
|
|
322
|
-
.avatar-status.status-
|
|
323
|
-
.avatar-status.status-
|
|
343
|
+
.avatar-status.status-idle { background: var(--st-ready-dot); }
|
|
344
|
+
.avatar-status.status-thinking { background: var(--st-thinking-dot); animation: pulse 1.2s ease-in-out infinite; }
|
|
345
|
+
.avatar-status.status-writing { background: var(--st-writing-dot); animation: pulse 1.2s ease-in-out infinite; }
|
|
346
|
+
.avatar-status.status-offline { background: var(--st-asleep-dot); }
|
|
347
|
+
.avatar-status.status-error { background: var(--st-error-dot); }
|
|
348
|
+
.avatar-status.status-starting { background: var(--st-waiting-dot); }
|
|
349
|
+
.avatar-status.status-queued { background: var(--st-waiting-dot); }
|
|
324
350
|
.avatar-picker { display: grid; grid-template-columns: repeat(auto-fill, minmax(38px, 1fr)); gap: 6px; margin: 4px 0 8px; max-height: 148px; overflow-y: auto; padding: 2px 4px 2px 2px; scroll-padding: 4px; }
|
|
325
351
|
.avatar-picker button { aspect-ratio: 1; width: 100%; border-radius: 12px; border: 2px solid transparent; background: var(--soft); font-size: 20px; padding: 0; display: inline-flex; align-items: center; justify-content: center; transition: transform var(--t-fast) var(--ease-pop), border-color var(--t-fast), background var(--t-fast); }
|
|
326
352
|
.avatar-picker button:hover { transform: translateY(-1px); background: var(--lav); }
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "Forge & Lumen",
|
|
3
|
-
"order": 1,
|
|
4
|
-
"emoji": "⚒️",
|
|
5
|
-
"recommended": true,
|
|
6
|
-
"description": "Two equals around one codebase. Forge builds by default, Lumen explains by default; either does either when asked, and either reviews the other's work when you ask for it. The way viberoom itself was built.",
|
|
7
|
-
"settings": {
|
|
8
|
-
"language": {
|
|
9
|
-
"mode": "follow-human"
|
|
10
|
-
},
|
|
11
|
-
"turnTaking": "parallel",
|
|
12
|
-
"agentsWakeEachOther": false,
|
|
13
|
-
"hopLimit": 12,
|
|
14
|
-
"customRules": "Both build, both explain, both review; @ decides who does what. Without @, a task goes to Forge and a question to Lumen.\nThe human decides when work is reviewed: ask the other one to review it. Unasked, the other answers questions and otherwise stays silent; it does not review, comment on or extend work it was not asked about. When asked to review, first say in one line what you will check, then check it by that criterion: a number, a live check, the risky case. The builder reports to the human, never to the other vibemate. A build is reported as: what changed (the files, and the commit if the project uses version control), how to verify it (a command, a script, a screenshot), what was not verified, what is left.\nMeasure before diagnosing; say no with a reason and a cheaper alternative; keep replies short.\nWork only on your own task, never on the other's; do not touch what the other is working on.\nReply only when addressed. On a message to everyone, only the one it concerns answers (a task: Forge; a question: Lumen); the other stays silent. Never add to, correct or comment on the other's reply unless the human asks.\nFollow the conventions of the project you work in (its instructions, notes, tests). Where it keeps decision notes, write the decision there before reporting it here.\nRead every proposal and analyse it; agree or reject only with a real argument. Never concede to be agreeable, never object to seem rigorous.\nReply in the human's language. Code identifiers, commands, file paths, protocol and tool names stay exactly as in the code; established technical terms (kernel, thread, commit, pull request, layout) stay in English."
|
|
15
|
-
},
|
|
16
|
-
"vibemates": [
|
|
17
|
-
{
|
|
18
|
-
"name": "Forge",
|
|
19
|
-
"tagline": "builds by default; explains and reviews when asked",
|
|
20
|
-
"role": "You are Forge, one of two equal vibemates, Forge and Lumen. You lean to building: a task without an address is yours; read the code before you change it and keep each change small. Everything else is in the room rules."
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"name": "Lumen",
|
|
24
|
-
"tagline": "explains by default; builds and reviews when asked",
|
|
25
|
-
"role": "You are Lumen, one of two equal vibemates, Forge and Lumen. You lean to explaining: a question without an address is yours; when you review, it is by a criterion, not an impression. Everything else is in the room rules."
|
|
26
|
-
}
|
|
27
|
-
]
|
|
28
|
-
}
|