lampson 0.1.3 → 0.2.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.
Files changed (55) hide show
  1. package/.env.example +9 -0
  2. package/README.md +87 -4
  3. package/bin/lampson.js +1 -1
  4. package/chat.syn +133 -0
  5. package/cli.syn +68 -0
  6. package/hub.tpl.syn +127 -0
  7. package/lampson.ps1 +77 -51
  8. package/lampson.sh +68 -22
  9. package/lib/agents.syn +1 -1
  10. package/lib/approvals.syn +179 -0
  11. package/lib/lsp.syn +10 -1
  12. package/lib/mcp.syn +10 -1
  13. package/lib/permission.syn +18 -0
  14. package/lib/sched_run.syn +161 -0
  15. package/lib/schedule.syn +660 -0
  16. package/lib/session.syn +38 -1
  17. package/lib/settings.syn +66 -3
  18. package/lib/skills.syn +2 -0
  19. package/lib/tools.syn +94 -2
  20. package/lib/workspaces.syn +555 -0
  21. package/package.json +3 -1
  22. package/public/css/chat.css +56 -0
  23. package/public/css/hub.css +12 -0
  24. package/public/css/layout.css +97 -0
  25. package/public/css/panel.css +125 -0
  26. package/public/css/sidebar.css +80 -0
  27. package/public/css/tokens.css +57 -0
  28. package/public/hub.html +36 -0
  29. package/public/index.html +51 -1197
  30. package/public/js/agents.js +31 -0
  31. package/public/js/app.js +21 -0
  32. package/public/js/approvals.js +26 -0
  33. package/public/js/chat.js +92 -0
  34. package/public/js/config.js +100 -0
  35. package/public/js/core.js +91 -0
  36. package/public/js/events.js +31 -0
  37. package/public/js/hub.js +40 -0
  38. package/public/js/lamps.js +112 -0
  39. package/public/js/lsp.js +81 -0
  40. package/public/js/mcp.js +74 -0
  41. package/public/js/memory.js +17 -0
  42. package/public/js/panel.js +101 -0
  43. package/public/js/procs.js +45 -0
  44. package/public/js/schedules.js +118 -0
  45. package/public/js/sessions.js +69 -0
  46. package/public/js/sidebar.js +33 -0
  47. package/public/js/terminal.js +49 -0
  48. package/public/js/theme.js +6 -0
  49. package/public/js/todo.js +10 -0
  50. package/public/js/tree.js +53 -0
  51. package/public/js/update.js +14 -0
  52. package/public/js/workspaces.js +83 -0
  53. package/skills/lampson/SKILL.md +21 -0
  54. package/skills/synsema/SKILL.md +4 -1
  55. package/web.syn +165 -55
@@ -0,0 +1,97 @@
1
+ /* layout: grid de tres columnas, cabecera, paneles laterales plegables, zona central (chat / visor / terminal), composer */
2
+ /* ---- grid: cabecera arriba, panel izquierdo (todo lo operativo), chat, panel derecho (árbol) ---- */
3
+ body { display:grid; grid-template-rows:auto 1fr; grid-template-columns:var(--side-l) 1fr var(--side-r); grid-template-areas:"top top top" "side main tree"; --side-l:var(--side); --side-r:var(--side); }
4
+ body.l-closed { --side-l:40px; }
5
+ body.r-closed { --side-r:40px; }
6
+
7
+ /* ---- header: fila 1 quién/dónde, fila 2 contexto y cómo corre el agente ---- */
8
+ header { grid-area:top; border-bottom:1px solid var(--rule); background:var(--paper); }
9
+ .hrow { display:flex; align-items:center; gap:14px; padding:0 18px; height:44px; min-width:0; }
10
+ .hrow.sub { height:32px; border-top:1px solid var(--rule); gap:12px; }
11
+ .brand { font:600 14px/1 var(--mono); color:var(--ink); letter-spacing:-.01em; white-space:nowrap; }
12
+ .brand .proj { color:var(--ink-3); font-weight:400; margin-left:.6em; }
13
+ .hbtn { display:flex; align-items:center; gap:6px; font:600 10.5px/1 var(--mono); text-transform:uppercase; letter-spacing:.09em; color:var(--ink-2); background:none; border:1px solid var(--rule-2); border-radius:2px; padding:7px 12px; cursor:pointer; }
14
+ .hbtn:hover { color:var(--ink); border-color:var(--ink-3); }
15
+ .hbtn.upd { color:var(--amber); border-color:var(--amber); }
16
+ .hbtn.upd:hover { color:var(--ink); border-color:var(--ink); }
17
+ .theme-toggle { display:flex; align-items:center; justify-content:center; background:none; border:0; color:var(--ink-3); cursor:pointer; padding:6px; border-radius:var(--r); line-height:0; }
18
+ .theme-toggle:hover { color:var(--ink); background:var(--paper-2); }
19
+ header select { font:400 11.5px/1 var(--mono); color:var(--ink-2); background:var(--paper); border:1px solid var(--rule); border-radius:var(--r); padding:5px 6px; cursor:pointer; }
20
+ header select:hover { border-color:var(--rule-2); color:var(--ink); }
21
+ .pill { font:400 11.5px/1 var(--mono); color:var(--ink-3); letter-spacing:.02em; white-space:nowrap; max-width:200px; overflow:hidden; text-overflow:ellipsis; flex:none; cursor:pointer; border-bottom:1px dashed transparent; }
22
+ .pill:hover { color:var(--ink); border-bottom-color:var(--rule-2); }
23
+ .pill.nokey { color:var(--rubric); }
24
+ .pill.on { color:var(--accent); }
25
+
26
+ .gitchip { display:none; align-items:center; gap:8px; font:400 11.5px/1 var(--mono); color:var(--ink-2); max-width:45%; flex:none; }
27
+ .gitchip .br { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
28
+ .gitchip b { font-weight:600; }
29
+ .gitchip .w { color:var(--amber); } .gitchip .g { color:var(--str); } .gitchip .r { color:var(--rubric); } .gitchip .c { color:var(--ink-3); font-weight:400; }
30
+ .hpath { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--ink-3); font:400 11.5px/1 var(--mono); }
31
+
32
+ /* ---- paneles laterales: iguales, plegables (cerrado = barra angosta con su icono) ---- */
33
+ aside { grid-area:side; position:relative; border-right:1px solid var(--rule); background:var(--paper); display:flex; flex-direction:column; min-height:0; overflow:hidden; }
34
+ aside.right { grid-area:tree; border-right:0; border-left:1px solid var(--rule); }
35
+ aside .rail { display:none; flex-direction:column; align-items:center; padding-top:10px; gap:8px; }
36
+ aside .rail button, aside .fold { background:none; border:0; color:var(--ink-3); cursor:pointer; padding:5px; border-radius:var(--r); line-height:0; }
37
+ aside .rail button:hover, aside .fold:hover { color:var(--ink); background:var(--paper-2); }
38
+ aside .fold { position:absolute; top:7px; right:6px; z-index:2; }
39
+ aside.right h2 { padding-right:56px; }
40
+ aside:not(.right) > .sec:first-of-type h2 { padding-right:60px; }
41
+ aside.closed .sec, aside.closed .fold { display:none; }
42
+ aside.closed .rail { display:flex; }
43
+
44
+ /* ---- main ---- */
45
+ main { grid-area:main; display:grid; grid-template-rows:1fr auto; min-height:0; min-width:0; background:var(--paper); }
46
+ #stage { overflow:auto; min-height:0; }
47
+ #log { padding:26px 40px 30px; max-width:920px; }
48
+ #log.hero { max-width:none; min-height:100%; display:flex; align-items:center; padding:20px 40px; }
49
+ #viewer { display:none; padding:0; }
50
+ .vh { position:sticky; top:0; display:flex; gap:12px; align-items:center; padding:9px 16px; background:var(--paper-3); border-bottom:1px solid var(--rule); font:400 11.5px/1 var(--mono); color:var(--ink-3); letter-spacing:.03em; }
51
+ .vh b { color:var(--ink); font-weight:400; }
52
+ .vh .x { cursor:pointer; color:var(--ink-3); }
53
+ .vh .x:hover { color:var(--ink); }
54
+ #viewer pre { margin:0; padding:14px 18px; font:400 12.5px/1.62 var(--mono); tab-size:4; }
55
+ #viewer pre .ln { display:inline-block; width:4ch; margin-right:16px; color:var(--ink-3); text-align:right; user-select:none; }
56
+ #viewer pre.log { color:var(--ink-2); white-space:pre-wrap; }
57
+ /* terminal real (xterm.js sobre un pty del servidor) — ocupa la zona central como un archivo abierto */
58
+ #termpane { display:none; height:100%; flex-direction:column; }
59
+ #termpane .vh { position:static; }
60
+ #termpane .vh .st { width:7px; height:7px; border-radius:50%; background:var(--ink-3); }
61
+ #termpane.live .vh .st { background:var(--str); }
62
+ #xterm { flex:1; min-height:0; padding:10px 14px; background:var(--paper); }
63
+ #xterm .xterm { height:100%; }
64
+ #xterm .xterm-viewport { background:transparent !important; }
65
+
66
+ /* ---- composer ---- */
67
+ form { padding:12px 40px 12px; border-top:1px solid var(--rule); background:var(--paper); box-sizing:border-box; width:100%; max-width:100%; min-width:0; }
68
+ main > div { min-width:0; }
69
+ .composer { background:var(--paper-2); border:1px solid var(--rule); border-radius:var(--r); padding:10px 10px 8px 14px; transition:border-color .12s, box-shadow .12s; }
70
+ .composer:focus-within { border-color:var(--rule-2); box-shadow:0 0 0 3px var(--accent-bg); }
71
+ textarea { display:block; width:100%; resize:none; min-height:40px; max-height:220px; background:transparent; color:var(--ink); border:0; padding:2px 0; font:400 15px/1.55 var(--serif); }
72
+ textarea::placeholder { color:var(--ink-3); }
73
+ textarea:focus { outline:none; }
74
+ .crow { display:flex; align-items:center; gap:10px; margin-top:6px; min-width:0; }
75
+ .crow .tools { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
76
+ .crow .keys, .crow button { flex:none; }
77
+ /* imágenes pegadas: miniaturas en el composer y en el mensaje */
78
+ .attach { display:flex; gap:8px; flex-wrap:wrap; }
79
+ .attach:not(:empty) { margin:2px 0 8px; }
80
+ .attach .th, .user .th { position:relative; width:72px; height:72px; border:1px solid var(--rule); border-radius:var(--r); overflow:hidden; background:var(--paper-3); }
81
+ .attach .th img, .user .th img { width:100%; height:100%; object-fit:cover; display:block; }
82
+ .attach .th .rm { position:absolute; top:2px; right:2px; width:18px; height:18px; border-radius:50%; background:var(--paper); color:var(--ink-2); font:600 11px/18px var(--mono); text-align:center; cursor:pointer; border:1px solid var(--rule-2); }
83
+ .attach .th .rm:hover { color:var(--rubric); }
84
+ .attach .th .sz { position:absolute; left:0; right:0; bottom:0; background:rgba(0,0,0,.55); color:#fff; font:400 9.5px/1.4 var(--mono); text-align:center; }
85
+ .user .ims { display:flex; gap:8px; flex-wrap:wrap; margin-top:8px; }
86
+ .user .th { width:120px; height:90px; cursor:zoom-in; }
87
+ .composer.drop { border-color:var(--accent); }
88
+ .attach .novision { flex-basis:100%; color:var(--amber); font:400 11.5px/1.5 var(--mono); }
89
+ .crow .tools { color:var(--ink-3); font:400 11px/1 var(--mono); letter-spacing:.03em; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
90
+ .crow .keys { color:var(--ink-3); font:400 10.5px/1 var(--mono); letter-spacing:.03em; }
91
+ #send { width:34px; height:30px; padding:0; font-size:15px; display:inline-flex; align-items:center; justify-content:center; }
92
+ #stop { color:var(--rubric); border-color:var(--rubric); display:none; height:30px; }
93
+ form.busy #send { display:none; } form.busy #stop { display:inline-flex; align-items:center; gap:6px; }
94
+ .hint { display:none; }
95
+
96
+ @media (max-width:900px) { body { grid-template-columns:1fr; grid-template-areas:"top" "main"; } aside { display:none; } #log, form { padding-left:20px; padding-right:20px; } }
97
+ @media (prefers-reduced-motion:reduce) { *, *::before, *::after { transition:none !important; animation:none !important; } }
@@ -0,0 +1,125 @@
1
+ /* panel: EL componente modal de la app (js/panel.js). Un cascarón (overlay, cabecera, ✕/Esc/clic afuera) y
2
+ tres layouts: browse (buscador + lista + detalle), tabs (pestañas con formularios) y form (un formulario).
3
+ Cada vista (sesiones, lámparas, configuración, programar…) solo aporta su contenido; nada de esto se repite. */
4
+ .modal { position:fixed; inset:0; background:rgba(0,0,0,.45); display:flex; align-items:center; justify-content:center; z-index:50; }
5
+ .panel { background:var(--paper); border:1px solid var(--rule-2); border-radius:var(--r); box-shadow:0 10px 30px rgba(0,0,0,.35); display:flex; flex-direction:column; min-height:0; max-height:92vh; }
6
+ .panel.lg { width:min(1100px, 94vw); height:min(760px, 90vh); padding:14px 18px 12px; }
7
+ .panel.md { width:min(680px, 94vw); padding:14px 18px 12px; }
8
+ .panel.sm { width:min(560px, 92vw); padding:14px 18px 12px; }
9
+
10
+ /* cabecera */
11
+ .panel .phead { display:flex; align-items:center; gap:12px; padding-bottom:10px; border-bottom:1px solid var(--rule); flex:none; }
12
+ .panel .phead .eyebrow { font:400 10.5px/1 var(--mono); text-transform:uppercase; letter-spacing:.12em; color:var(--rubric); margin:0; }
13
+ .panel .phead h3 { margin:0; font:600 16px/1.2 var(--serif); letter-spacing:-.01em; }
14
+ .panel .phead .sub { color:var(--ink-3); font:400 11.5px/1 var(--mono); }
15
+ .panel .pghost { display:inline-flex; align-items:center; justify-content:center; width:26px; height:26px; padding:0; margin:0; font:400 13px/1 var(--mono); letter-spacing:0; text-transform:none; border:1px solid var(--rule); border-radius:var(--r); background:transparent; color:var(--ink-3); cursor:pointer; }
16
+ .panel .pghost:hover { color:var(--ink); border-color:var(--rule-2); }
17
+ .panel .pghost + .pghost { margin-left:6px; }
18
+
19
+ /* pestañas */
20
+ .panel .ptabs { display:flex; gap:2px; border-bottom:1px solid var(--rule); margin:0 0 14px; flex:none; }
21
+ .panel .ptab { font:400 11px/1 var(--mono); text-transform:uppercase; letter-spacing:.08em; color:var(--ink-3); padding:10px 12px 8px; cursor:pointer; border-bottom:2px solid transparent; margin-bottom:-1px; }
22
+ .panel .ptab:hover { color:var(--ink); }
23
+ .panel .ptab.on { color:var(--accent); border-bottom-color:var(--accent); }
24
+
25
+ /* cuerpo genérico (form / tabs): prosa y campos con la gramática de la app */
26
+ .panel .pbody { flex:1; min-height:0; overflow:auto; padding:14px 2px 4px; }
27
+ .panel .lead { color:var(--ink-2); margin:0 0 16px; font-size:14px; }
28
+ .panel .lead code, .panel .phelp code { font:400 .88em/1.5 var(--mono); background:var(--paper-2); padding:.08em .3em; border-radius:2px; color:var(--ink); }
29
+ .panel label { display:flex; align-items:center; gap:12px; font:400 10.5px/1 var(--mono); text-transform:uppercase; letter-spacing:.1em; color:var(--ink-3); margin:0 0 10px; }
30
+ .panel label.col { flex-direction:column; align-items:stretch; gap:4px; }
31
+ .panel label .ds { display:block; color:var(--ink-3); font:400 11.5px/1.4 var(--serif); text-transform:none; letter-spacing:0; margin-top:2px; }
32
+ .panel input, .panel select, .panel textarea { flex:1; font:400 13px/1.4 var(--mono); color:var(--ink); background:var(--paper-2); border:1px solid var(--rule); border-radius:var(--r); padding:7px 10px; min-width:0; }
33
+ .panel input:focus, .panel select:focus, .panel textarea:focus { outline:none; border-color:var(--rule-2); box-shadow:0 0 0 3px var(--accent-bg); }
34
+ .panel input[disabled] { opacity:.55; }
35
+ .panel textarea { width:100%; resize:vertical; font:400 12.5px/1.45 var(--mono); }
36
+ .panel .pfoot { display:flex; gap:8px; align-items:center; margin-top:14px; flex:none; }
37
+ .panel .err { color:var(--rubric); font:400 12px/1.4 var(--mono); min-height:14px; }
38
+ .panel .plist { border-top:1px solid var(--rule); border-bottom:1px solid var(--rule); max-height:40vh; overflow:auto; margin:0 0 14px; }
39
+ .panel .plist .pr { display:flex; align-items:center; gap:10px; padding:7px 10px; cursor:pointer; font:400 12.5px/1.4 var(--mono); color:var(--ink-2); border-left:2px solid transparent; }
40
+ .panel .plist .pr:hover { color:var(--ink); }
41
+ .panel .plist .pr.on { color:var(--ink); border-left-color:var(--accent); background:var(--paper-2); }
42
+ .panel .plist .pr b { width:110px; font-weight:600; color:var(--ink); }
43
+ .panel .plist .pr .dm { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--ink-3); }
44
+ .panel .plist .pr .k { font-size:11px; color:var(--str); }
45
+ .panel .plist .pr .k.no { color:var(--ink-3); }
46
+
47
+ /* browse: lista a la izquierda (buscador, filas, contador), detalle a la derecha. Un scroll por columna, nunca horizontal. */
48
+ .panel .browse { flex:1; min-height:0; display:grid; grid-template-columns:var(--list-w, 300px) minmax(0,1fr); }
49
+ .panel .blist { display:flex; flex-direction:column; border-right:1px solid var(--rule); padding:10px 12px 8px 0; min-height:0; min-width:0; }
50
+ .panel .blist input { flex:none; height:30px; font:400 12px/1.4 var(--mono); background:transparent; padding:0 8px; margin:0 0 8px; width:100%; }
51
+ .panel .bitems { flex:1; overflow-y:auto; overflow-x:hidden; min-height:0; }
52
+ .panel .bcount { color:var(--ink-3); font:400 11px/1 var(--mono); padding-top:8px; border-top:1px solid var(--rule); flex:none; }
53
+ .panel .bdetail { overflow-y:auto; overflow-x:hidden; min-height:0; min-width:0; padding:12px 6px 8px 18px; font:400 12.5px/1.5 var(--mono); }
54
+ .panel .bempty { color:var(--ink-3); font:400 12.5px/1.5 var(--serif); padding:8px; }
55
+ .panel .li { display:grid; grid-template-columns:14px minmax(0,1fr); gap:8px; align-items:start; padding:7px 8px; border-radius:var(--r); cursor:pointer; font:400 12px/1.35 var(--mono); min-width:0; }
56
+ .panel .li.nodot { grid-template-columns:minmax(0,1fr); }
57
+ .panel .li > div { min-width:0; }
58
+ .panel .li:hover { background:var(--paper-2); }
59
+ .panel .li.sel { background:var(--paper-2); box-shadow:inset 2px 0 0 var(--accent); }
60
+ .panel .li .dot { color:var(--ink-3); margin-top:1px; }
61
+ .panel .li.on .dot { color:var(--accent); }
62
+ .panel .li.bad .dot { color:var(--rubric); }
63
+ .panel .li.new .nm { color:var(--accent); font-weight:400; }
64
+ .panel .dform label { margin-top:10px; }
65
+ .panel .dform .lead { margin:8px 0 12px; }
66
+ .panel .dform .pfoot { margin-top:16px; }
67
+ .panel .derr { color:var(--rubric); font:400 12px/1.4 var(--mono); min-height:14px; margin-top:6px; }
68
+ .panel .dlog { margin-top:12px; max-height:38vh; overflow:auto; font:400 11.5px/1.45 var(--mono); border:1px solid var(--rule); border-radius:var(--r); padding:8px; white-space:pre-wrap; color:var(--ink-2); }
69
+ .panel .li.broken .dot { color:var(--rubric); }
70
+ .panel .li .nm { color:var(--ink); font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
71
+ .panel .li .nm.serif { font:400 13px/1.35 var(--serif); }
72
+ .panel .li .meta { color:var(--ink-3); font-size:11px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
73
+ .panel .li .snip { color:var(--ink-2); font:400 12px/1.4 var(--serif); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
74
+ .panel mark { background:transparent; color:var(--accent); font-weight:600; }
75
+
76
+ /* piezas de detalle que comparten las vistas */
77
+ .panel .dhead { display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
78
+ .panel .dhead .nm { font:600 18px/1.2 var(--mono); color:var(--ink); }
79
+ .panel .dhead .nm.serif { font:600 16px/1.3 var(--serif); }
80
+ .panel .dhead .meta { color:var(--ink-3); font-size:11.5px; }
81
+ .panel .ddesc { color:var(--ink-2); font:400 13px/1.5 var(--serif); margin:8px 0 4px; max-width:70ch; }
82
+ .panel .dcap { color:var(--ink-3); font-size:11.5px; margin-bottom:12px; word-break:break-all; }
83
+ .panel .dcap b { color:var(--rubric); font-weight:600; }
84
+ .panel .dcap.err { color:var(--rubric); }
85
+ .panel .dnote { color:var(--ink-3); font:400 12px/1.5 var(--serif); border:1px dashed var(--rule-2); border-radius:var(--r); padding:8px 10px; margin:8px 0 12px; }
86
+ .panel .dacts { display:flex; gap:8px; margin-top:12px; }
87
+ .panel .dacts button { font:400 12px/1 var(--mono); padding:6px 12px; }
88
+ .panel .dfoot { display:flex; align-items:center; gap:12px; margin-top:14px; padding-top:10px; border-top:1px solid var(--rule); color:var(--ink-3); font-size:11.5px; }
89
+ .panel .dfoot .del { margin-left:auto; cursor:pointer; color:var(--rubric); }
90
+ .panel .dfoot .del.ask .yes { color:var(--rubric); cursor:pointer; font-weight:600; }
91
+ .panel .dfoot .del.ask .no { cursor:pointer; }
92
+ .panel .none { color:var(--ink-3); font:400 13px/1.6 var(--serif); max-width:520px; }
93
+ /* switch encendido/apagado (lámparas) */
94
+ .panel label.sw { display:inline-flex; align-items:center; margin:0 0 0 auto; gap:6px; cursor:pointer; user-select:none; font:400 11.5px/1 var(--mono); text-transform:none; letter-spacing:0; color:var(--ink-3); }
95
+ .panel label.sw input { flex:none; width:16px; height:16px; padding:0; margin:0; accent-color:var(--accent); }
96
+ .panel label.sw.on { color:var(--accent); }
97
+ /* formulario generado desde un JSON Schema (tools de una lámpara) */
98
+ .panel .tool { border:1px solid var(--rule); border-radius:var(--r); padding:10px 12px; margin-bottom:10px; }
99
+ .panel .tool .th { display:grid; grid-template-columns:auto 1fr auto; gap:8px 10px; align-items:baseline; }
100
+ .panel .tool .th code { font-weight:600; color:var(--ink); }
101
+ .panel .tool .th .ds { color:var(--ink-3); font:400 12px/1.4 var(--serif); }
102
+ .panel label.lf { display:grid; grid-template-columns:120px minmax(0,1fr); gap:2px 10px; align-items:center; margin:8px 0 0; font:400 12px/1.4 var(--mono); text-transform:none; letter-spacing:0; color:var(--ink); }
103
+ .panel label.lf .lk { color:var(--ink); }
104
+ .panel label.lf .req { color:var(--rubric); font-size:10.5px; }
105
+ .panel label.lf .ds { grid-column:2; color:var(--ink-3); font:400 11.5px/1.4 var(--serif); }
106
+ .panel label.lf input, .panel label.lf select, .panel label.lf textarea { flex:none; width:100%; max-width:420px; padding:4px 6px; font:400 12px/1.4 var(--mono); background:transparent; }
107
+ .panel label.lf textarea { grid-column:2; min-height:56px; }
108
+ .panel .tool .go { margin-top:10px; display:flex; align-items:center; gap:10px; }
109
+ .panel .tool .go button { font:400 12px/1 var(--mono); padding:5px 10px; }
110
+ .panel .tool .go .st { color:var(--ink-3); font-size:11.5px; }
111
+ .panel .tool pre.out { margin-top:8px; max-height:38vh; overflow:auto; font:400 11.5px/1.45 var(--mono); border:1px solid var(--rule); border-radius:var(--r); padding:8px; white-space:pre-wrap; }
112
+ /* conversación resumida (sesiones) */
113
+ .panel .dmsgs { margin-top:12px; display:flex; flex-direction:column; gap:8px; }
114
+ .panel .dmsg { border-left:2px solid var(--rule-2); padding:2px 0 2px 10px; font:400 12.5px/1.5 var(--serif); color:var(--ink-2); white-space:pre-wrap; word-break:break-word; }
115
+ .panel .dmsg.dm-user { border-left-color:var(--accent); color:var(--ink); }
116
+ .panel .dmsg.dm-bad { border-left-color:var(--rubric); }
117
+ .panel .dmsg .who a { color:var(--accent); text-decoration:none; }
118
+ .panel .dmsg .who { font:400 10.5px/1 var(--mono); text-transform:uppercase; letter-spacing:.08em; color:var(--ink-3); display:block; margin-bottom:3px; }
119
+ /* ayuda («?»): prosa a lo ancho, reemplaza al cuerpo */
120
+ .panel .phelp { flex:1; min-height:0; overflow:auto; padding:16px 24px 8px 6px; font:400 13.5px/1.6 var(--serif); color:var(--ink-2); }
121
+ .panel .phelp h4 { margin:0 0 8px; font:600 15px/1.3 var(--serif); color:var(--ink); }
122
+ .panel .phelp b { color:var(--ink); font-weight:600; }
123
+ .panel .phelp p { margin:0 0 12px; max-width:78ch; }
124
+ .panel .phelp ul { margin:0 0 16px 20px; padding:0; display:grid; gap:4px; max-width:78ch; }
125
+ .panel .phelp button { text-transform:none; letter-spacing:0; font:400 12px/1 var(--mono); padding:6px 10px; }
@@ -0,0 +1,80 @@
1
+ /* sidebar: secciones acordeón y las filas de cada una */
2
+ /* secciones colapsables; el activo es una muesca en la regla, no una caja */
3
+ .sec { display:flex; flex-direction:column; min-height:0; flex:none; overflow:hidden; }
4
+ .sec + .sec { border-top:1px solid var(--rule); }
5
+ .sec h2 { font:400 10.5px/1.35 var(--mono); text-transform:uppercase; letter-spacing:.1em; color:var(--ink-3); margin:0; padding:12px 16px 8px; display:flex; align-items:center; gap:8px; cursor:pointer; user-select:none; flex:none; }
6
+ .sec h2:hover { color:var(--ink); }
7
+ .sec .caret { width:9px; font-size:9px; color:var(--ink-3); }
8
+ .sec h2 .h2act { margin-left:auto; border:0; background:none; color:var(--ink-3); font:inherit; font-size:12px; cursor:pointer; padding:0 2px; line-height:1; opacity:.6; }
9
+ .sec h2 .h2act:hover { opacity:1; color:var(--accent); }
10
+ .sec h2 .h2act.spin { animation: h2spin .6s linear; }
11
+ @keyframes h2spin { to { transform: rotate(360deg); } }
12
+ .sec .cnt { margin-left:auto; letter-spacing:0; text-transform:none; }
13
+ .sec .body { display:none; overflow:auto; min-height:0; padding:0 10px 10px 16px; }
14
+ .sec.open .body { display:block; }
15
+ .sec:not(.grow).open .body { max-height:22vh; }
16
+ .sec.grow.open { flex:1; }
17
+ .sec.grow.open .body { flex:1; }
18
+ #tree { font:400 12.5px/1.5 var(--mono); }
19
+ .node { user-select:none; }
20
+ .row { display:flex; align-items:center; gap:6px; padding:3px 6px 3px 8px; cursor:pointer; color:var(--ink-2); white-space:nowrap; margin-left:-1px; border-left:2px solid transparent; }
21
+ .row:hover { color:var(--ink); }
22
+ .row.dir { color:var(--ink); }
23
+ .row .tw { width:9px; color:var(--ink-3); font-size:9px; }
24
+ .row.active { color:var(--ink); font-weight:600; border-left-color:var(--accent); }
25
+ .row.gM > span:nth-child(2) { color:var(--amber); }
26
+ .row.gA > span:nth-child(2), .row.gU > span:nth-child(2) { color:var(--str); }
27
+ .row.gD > span:nth-child(2) { color:var(--rubric); text-decoration:line-through; }
28
+ .row .gs { margin-left:auto; font-size:10px; color:var(--ink-3); }
29
+ .kids { margin-left:12px; border-left:1px solid var(--rule); }
30
+ .kids.hidden { display:none; }
31
+ .s, .m, .p { display:flex; align-items:center; gap:8px; padding:4px 6px 4px 8px; cursor:pointer; color:var(--ink-2); font:400 12.5px/1.5 var(--mono); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; margin-left:-1px; border-left:2px solid transparent; }
32
+ .s:hover, .m:hover, .p:hover { color:var(--ink); }
33
+ .s.active, .m.active, .p.active { color:var(--ink); border-left-color:var(--accent); }
34
+ .s .title { overflow:hidden; text-overflow:ellipsis; font-family:var(--serif); font-size:13px; }
35
+ .s .id { color:var(--ink-3); font-size:11px; flex:none; }
36
+ .m b { color:var(--ink); font-weight:600; flex:none; }
37
+ .m .t { overflow:hidden; text-overflow:ellipsis; font-family:var(--serif); font-size:13px; }
38
+ .p .st { width:7px; height:7px; border-radius:50%; background:var(--ink-3); flex:none; }
39
+ .p.run .st { background:var(--str); }
40
+ .p .nm { font-weight:600; color:var(--ink); }
41
+ .p .cm { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1; }
42
+ .p .port { color:var(--amber); font-weight:600; text-decoration:none; flex:none; }
43
+ .p .port:hover { text-decoration:underline; text-underline-offset:.18em; }
44
+ .p button { padding:1px 7px; font-size:10px; }
45
+ .none { color:var(--ink-3); cursor:default; }
46
+ .none:hover { color:var(--ink-3); }
47
+
48
+ /* filas de tareas programadas y aprobaciones */
49
+ .p.sched { flex-wrap:wrap; }
50
+ .p.sched .st.on { background:var(--accent); }
51
+ .p.sched .st.busy { background:var(--amber); animation: pulse 1s ease-in-out infinite; }
52
+ .p.sched .st.bad { background:var(--rubric); }
53
+ .p.sched .sub { flex-basis:100%; color:var(--ink-3); font-size:10.5px; padding-left:15px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
54
+ .p.sched .act { margin-left:auto; display:inline-flex; gap:2px; visibility:hidden; }
55
+ .p.sched:hover .act, .p.sched .act.ask { visibility:visible; }
56
+ .p.sched .act span { color:var(--ink-3); font-size:11px; padding:0 4px; cursor:pointer; }
57
+ .p.sched .act span:hover { color:var(--accent); }
58
+ .p.sched .act span.rm:hover { color:var(--rubric); }
59
+ .p.appr { flex-wrap:wrap; cursor:default; }
60
+ .p.appr .msg { flex-basis:100%; color:var(--ink); font-size:11.5px; white-space:pre-wrap; padding-left:15px; }
61
+ .p.appr .why { flex-basis:100%; color:var(--rubric); font-size:10.5px; padding-left:15px; }
62
+ .p.appr .btns { flex-basis:100%; display:flex; gap:6px; padding:4px 0 2px 15px; }
63
+ .p.appr .btns button { padding:2px 9px; font-size:11px; }
64
+ .sec .daemon { font:400 10.5px/1.5 var(--mono); color:var(--ink-3); padding:2px 0 4px; }
65
+ .sec .daemon.off { color:var(--rubric); }
66
+ @keyframes pulse { 0%,100% { opacity:1 } 50% { opacity:.35 } }
67
+
68
+ .s.more { color:var(--accent); }
69
+
70
+ /* ✕ → «¿borrar? sí · no» en línea */
71
+ .s .del { margin-left:auto; color:var(--ink-3); visibility:hidden; font-size:11px; padding:0 4px; }
72
+ .s:hover .del { visibility:visible; }
73
+ .s .del:hover { color:var(--rubric); }
74
+ .p .del { margin-left:auto; color:var(--ink-3); visibility:hidden; font-size:11px; padding:0 4px; cursor:pointer; }
75
+ .p:hover .del { visibility:visible; }
76
+ .p .del:hover { color:var(--rubric); }
77
+ .del.ask, .s:hover .del.ask { visibility:visible; color:var(--ink-2); font:400 11px/1.4 var(--mono); white-space:nowrap; }
78
+ .del.ask .yes { color:var(--rubric); cursor:pointer; font-weight:600; }
79
+ .del.ask .no { color:var(--ink-2); cursor:pointer; }
80
+ .del.ask .yes:hover, .del.ask .no:hover { text-decoration:underline; text-underline-offset:.18em; }
@@ -0,0 +1,57 @@
1
+ /* tokens: fuentes, paleta (dark/light), base y botones — lo que todo lo demás asume */
2
+ /* Misma dirección que docs.synsema.com: tinta sobre papel, una superfamilia (IBM Plex) en dos
3
+ voces — la SERIF para lo que se lee (mensajes, hero), la MONO para estructura y maquinaria
4
+ (header, sidebar, pasos, código, controles). El color se raciona: azul = link / dónde estás,
5
+ rubric rojo = peligro y permisos (la única cosa que vale la pena ver desde lejos). */
6
+ @font-face{font-family:"Plex Serif";font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-serif-400-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}
7
+ @font-face{font-family:"Plex Serif";font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-serif-400-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}
8
+ @font-face{font-family:"Plex Serif";font-style:italic;font-weight:400;font-display:swap;src:url(/fonts/plex-serif-400i-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}
9
+ @font-face{font-family:"Plex Serif";font-style:italic;font-weight:400;font-display:swap;src:url(/fonts/plex-serif-400i-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}
10
+ @font-face{font-family:"Plex Serif";font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-serif-600-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}
11
+ @font-face{font-family:"Plex Serif";font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-serif-600-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}
12
+ @font-face{font-family:"Plex Mono";font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-mono-400-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}
13
+ @font-face{font-family:"Plex Mono";font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-mono-400-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}
14
+ @font-face{font-family:"Plex Mono";font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-mono-600-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}
15
+ @font-face{font-family:"Plex Mono";font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-mono-600-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}
16
+
17
+ :root, :root[data-theme=dark] {
18
+ --paper:#131412; --paper-2:#1b1c19; --paper-3:#212320;
19
+ --ink:#e9e8df; --ink-2:#9b9c90; --ink-3:#848678;
20
+ --rule:#2b2c27; --rule-2:#3c3d36;
21
+ --accent:#8fb4dc; --accent-bg:#1b2836;
22
+ --rubric:#d98670; --str:#a8bd84; --amber:#d9b26f;
23
+ --sel:#33465b; --term-blue:#4f86d1;
24
+ --serif:"Plex Serif",Georgia,"Times New Roman",serif;
25
+ --mono:"Plex Mono",ui-monospace,SFMono-Regular,Consolas,"Liberation Mono",monospace;
26
+ --r:3px; --side:280px;
27
+ }
28
+ :root[data-theme=light] {
29
+ --paper:#fcfcfa; --paper-2:#f4f3ee; --paper-3:#ecebe3;
30
+ --ink:#1a1b18; --ink-2:#56584f; --ink-3:#6a6c61;
31
+ --rule:#e4e3da; --rule-2:#cfcec2;
32
+ --accent:#21578c; --accent-bg:#e9eef4;
33
+ --rubric:#99372a; --str:#4a6b3f; --amber:#8a6a1f;
34
+ --sel:#cfe0f0; --term-blue:#21578c;
35
+ }
36
+
37
+ * { box-sizing:border-box; }
38
+ html, body { height:100%; }
39
+ body { margin:0; background:var(--paper); color:var(--ink); font:400 14.5px/1.6 var(--serif); -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; }
40
+ ::selection { background:var(--sel); }
41
+ :focus-visible { outline:2px solid var(--accent); outline-offset:2px; border-radius:1px; }
42
+ button, select, textarea { font-family:inherit; }
43
+ .spacer { flex:1; }
44
+
45
+ /* botones: el primario es tinta sobre papel, el secundario solo borde */
46
+ button { font:600 10.5px/1 var(--mono); text-transform:uppercase; letter-spacing:.09em; color:var(--ink-2); background:none; border:1px solid var(--rule-2); border-radius:2px; padding:7px 12px; cursor:pointer; }
47
+ button:hover { color:var(--ink); border-color:var(--ink-3); }
48
+ button.primary { color:var(--paper); background:var(--ink); border-color:var(--ink); }
49
+ button.primary:hover { opacity:.85; color:var(--paper); }
50
+ button:disabled { opacity:.45; cursor:default; }
51
+
52
+ /* barras de scroll finas y del color del tema (las nativas de Windows son gruesas y grises) */
53
+ * { scrollbar-width:thin; scrollbar-color:var(--rule-2) transparent; }
54
+ *::-webkit-scrollbar { width:7px; height:7px; }
55
+ *::-webkit-scrollbar-track { background:transparent; }
56
+ *::-webkit-scrollbar-thumb { background:var(--rule-2); border-radius:4px; }
57
+ *::-webkit-scrollbar-thumb:hover { background:var(--ink-3); }
@@ -0,0 +1,36 @@
1
+ <!doctype html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Lampson · workspaces</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <link rel="stylesheet" href="/css/tokens.css">
8
+ <link rel="stylesheet" href="/css/layout.css">
9
+ <link rel="stylesheet" href="/css/chat.css">
10
+ <link rel="stylesheet" href="/css/panel.css">
11
+ <link rel="stylesheet" href="/css/hub.css">
12
+ <script>(function(){try{var t=localStorage.getItem('lampson.theme');if(t!=='light'&&t!=='dark'){t='dark';}document.documentElement.setAttribute('data-theme',t);}catch(e){}})();</script>
13
+ </head>
14
+ <body class="hub">
15
+ <header>
16
+ <div class="hrow">
17
+ <span class="brand">lampson<span class="proj">workspaces</span></span>
18
+ <span class="spacer"></span>
19
+ <button class="theme-toggle" id="theme" type="button" title="cambiar tema"></button>
20
+ </div>
21
+ </header>
22
+ <main>
23
+ <div id="stage">
24
+ <div class="empty">
25
+ <p class="eyebrow">workspaces</p>
26
+ <h1>¿En qué proyecto trabajamos?</h1>
27
+ <p class="lead">Cada workspace es una carpeta de tu disco con su propio agente: sesiones, tareas programadas, lámparas, MCP. Corren en procesos separados, así podés tener varios abiertos a la vez. Los que tienen tareas encendidas siguen vivos aunque cierres todo.</p>
28
+ <div class="wsgrid" id="wsGrid"></div>
29
+ <p class="lead" style="margin-top:22px">Desde una terminal: <code>cd mi-proyecto &amp;&amp; lampson</code> (terminal) o <code>lampson --web</code> (esta web, en ese workspace).</p>
30
+ </div>
31
+ </div>
32
+ </main>
33
+ <script src="/js/panel.js"></script>
34
+ <script src="/js/hub.js"></script>
35
+ </body>
36
+ </html>