create-pmos 0.162.0 → 0.163.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/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/c3-sessions-panel-test.js +49 -0
- package/workspace/guide/console3-sessions.md +12 -0
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/c3-session-term.js +69 -3
- package/workspace/public/pm/c3-sheet.js +5 -4
- package/workspace/public/pm/console3.css +16 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.163.0",
|
|
4
4
|
"description": "Scaffold the PMOS portable kit — run the PMOS product-management workflow (initiative → lane-fit spec → contract → build → three gates, with work-type lanes for features, research, maintenance, incidents, and content) entirely inside your own repo, with all state as git-versioned files. Includes `workflow-sweep`, which audits the state every board derives from and separates real defects from work you simply have not done. `create-pmos workspace` also installs + launches the local Terminal Workspace GUI. No database, no network, no telemetry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
package/payload/KIT-VERSION
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Eval calibration log"
|
|
4
4
|
description: "Append-only record of every PM override of an evaluator verdict."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T10:13:23Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Health budgets"
|
|
4
4
|
description: "Closed registry of health-budget anchors (D59), edited only by the PM."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T10:13:23Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Discoveries and friction log"
|
|
4
4
|
description: "Append-only record of what runs discovered and where the process rubbed."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T10:13:23Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -110,6 +110,55 @@ function startShell(name) {
|
|
|
110
110
|
ok('C4 the close button closes the terminal sheet', await waitFor(page, () => !document.querySelector('[data-c3-session-term]')));
|
|
111
111
|
ok('C5 closing the sheet leaves the session running', await until(async () => ((await get('/api/sessions')).sessions || []).some((s) => s.id === id && s.state !== 'exited')));
|
|
112
112
|
|
|
113
|
+
/* ── G: the scratchpad and the size control on the terminal sheet (c3-term-pad-size) ── */
|
|
114
|
+
await page.evaluate((i) => window.dispatchEvent(new CustomEvent('pmos:open-session', { detail: { id: i } })), id);
|
|
115
|
+
const g0 = await waitFor(page, (i) => !!document.querySelector(`[data-c3-session-term="${i}"] .xterm`), id);
|
|
116
|
+
ok('G0 the terminal sheet opens for the pad checks', g0 || !!(await page.$('[data-c3-size-seg]')));
|
|
117
|
+
const sizes = await page.$$eval('[data-c3-size-seg] [data-c3-size]', (b) => b.map((x) => x.dataset.c3Size));
|
|
118
|
+
ok('G1 the size control has 4 options', sizes.join() === 'normal,half,two-thirds,whole', sizes.join());
|
|
119
|
+
const vw = await page.evaluate(() => window.innerWidth);
|
|
120
|
+
const wid = () => page.evaluate(() => document.querySelector('[data-shell="sheet"] .sheet').getBoundingClientRect().width);
|
|
121
|
+
ok('G2 Normal is the 600px sheet', Math.abs((await wid()) - 600) < 2, String(await wid()));
|
|
122
|
+
await page.click('[data-c3-size="half"]');
|
|
123
|
+
await sleep(200);
|
|
124
|
+
ok('G3 Half makes the sheet half of the window', Math.abs((await wid()) - vw / 2) < 3, (await wid()) + ' of ' + vw);
|
|
125
|
+
ok('G3b Half is shown as pressed', await page.evaluate(() => document.querySelector('[data-c3-size="half"]').getAttribute('aria-pressed') === 'true'));
|
|
126
|
+
await page.click('[data-c3-size="whole"]');
|
|
127
|
+
await sleep(200);
|
|
128
|
+
ok('G3c Whole fills the window minus the margins', Math.abs((await wid()) - (vw - 24)) < 3, String(await wid()));
|
|
129
|
+
await page.click('[data-c3-size="half"]');
|
|
130
|
+
await page.click('[data-shell="sheet"] [aria-label="Close"]');
|
|
131
|
+
await waitFor(page, () => !document.querySelector('[data-c3-session-term]'));
|
|
132
|
+
await page.evaluate((i) => window.dispatchEvent(new CustomEvent('pmos:open-session', { detail: { id: i } })), id);
|
|
133
|
+
await waitFor(page, (i) => !!document.querySelector(`[data-c3-session-term="${i}"] .xterm`), id);
|
|
134
|
+
await sleep(200);
|
|
135
|
+
ok('G4 the size choice is kept after closing and opening again', Math.abs((await wid()) - vw / 2) < 3, String(await wid()));
|
|
136
|
+
ok('G4b it is kept in localStorage', await page.evaluate(() => localStorage.getItem('pmos.workspace.c3.term-size') === 'half'));
|
|
137
|
+
const termH = await page.evaluate(() => document.querySelector('[data-c3-session-term]').getBoundingClientRect().height);
|
|
138
|
+
ok('G5 the terminal has room above the scratchpad', termH > 120 && await page.evaluate(() => { const t = document.querySelector('[data-c3-session-term]').getBoundingClientRect(); const p = document.querySelector('[data-c3-pad="box"]').getBoundingClientRect(); return t.bottom <= p.top + 1; }));
|
|
139
|
+
await page.fill('[data-c3-pad="text"]', 'echo from-the-pad-7');
|
|
140
|
+
ok('G6 typing in the pad saves it for the session', !!(await until(async () => (await get('/api/scratchpad?session=' + id)).text === 'echo from-the-pad-7', 4000)));
|
|
141
|
+
await page.click(`[data-c3-session-term="${id}"] .xterm`);
|
|
142
|
+
await page.keyboard.press('Backspace'); // the Esc in C3 left the shell's readline waiting for a meta key: this key answers it
|
|
143
|
+
await page.fill('[data-c3-pad="text"]', 'echo from-the-pad-7');
|
|
144
|
+
await page.click('[data-c3-pad="send"]');
|
|
145
|
+
await sleep(800);
|
|
146
|
+
const noEnter = !/^from-the-pad-7/m.test((await get('/api/sessions/transcript?id=' + id + '&max=4000')).text || '');
|
|
147
|
+
await page.click(`[data-c3-session-term="${id}"] .xterm`);
|
|
148
|
+
await page.keyboard.press('Enter');
|
|
149
|
+
ok('G7 Send to terminal types the text into the session, without pressing Enter', noEnter && !!(await until(async () => /^from-the-pad-7/m.test((await get('/api/sessions/transcript?id=' + id + '&max=4000')).text || ''), 8000)));
|
|
150
|
+
await page.fill('[data-c3-pad="text"]', 'flushed-on-close');
|
|
151
|
+
await page.click('[data-shell="sheet"] [aria-label="Close"]');
|
|
152
|
+
ok('G8 closing the sheet saves a pad edit at once', !!(await until(async () => (await get('/api/scratchpad?session=' + id)).text === 'flushed-on-close', 2000)));
|
|
153
|
+
await page.evaluate((i) => window.dispatchEvent(new CustomEvent('pmos:open-session', { detail: { id: i } })), id);
|
|
154
|
+
await waitFor(page, () => !!document.querySelector('[data-c3-pad="text"]'));
|
|
155
|
+
ok('G9 the pad loads its text when the terminal opens again', await waitFor(page, () => document.querySelector('[data-c3-pad="text"]').value === 'flushed-on-close'));
|
|
156
|
+
await page.click('[data-c3-pad="toggle"]');
|
|
157
|
+
ok('G10 the toggle hides the pad and the terminal gets the room', await page.evaluate(() => document.querySelector('[data-c3-pad="text"]').hidden === true && localStorage.getItem('pmos.workspace.c3.term-pad') === 'closed'));
|
|
158
|
+
await page.click('[data-c3-pad="toggle"]');
|
|
159
|
+
await page.click('[data-shell="sheet"] [aria-label="Close"]');
|
|
160
|
+
await waitFor(page, () => !document.querySelector('[data-c3-session-term]'));
|
|
161
|
+
|
|
113
162
|
/* ── D: End asks first, then ends ── */
|
|
114
163
|
await page.click('[data-c3pg="agent-button"]');
|
|
115
164
|
ok('D0 the panel opens again', await waitFor(page, (s) => !!document.querySelector(s), sel));
|
|
@@ -44,6 +44,18 @@ If the server finds that the process of a session stopped (for example, after a
|
|
|
44
44
|
|
|
45
45
|
The terminal of the session opens in a panel on the same page. You can type in it. When the terminal has focus, Esc and Tab go to the terminal. To close the panel, press its close button. The session continues to run.
|
|
46
46
|
|
|
47
|
+
### The scratchpad
|
|
48
|
+
|
|
49
|
+
Below the terminal is a scratchpad. It keeps text for this session only. The text is saved on this machine as you type, and it is there when you open the terminal again.
|
|
50
|
+
|
|
51
|
+
- To send the text to the session, press **Send to terminal**. The text is typed into the terminal. Enter is not pressed, so you can read it and press Enter yourself.
|
|
52
|
+
- To hide the pad, press **Scratchpad** above it. The text is kept. The terminal gets the room back. The page remembers if the pad was hidden.
|
|
53
|
+
- Esc in the pad closes the panel. Esc in the terminal goes to the terminal.
|
|
54
|
+
|
|
55
|
+
### The size of the terminal
|
|
56
|
+
|
|
57
|
+
Four buttons in the header of the panel set its width: **Normal**, **Half**, **2/3** and **Whole**. Half is half of the window. 2/3 is two thirds of the window. Whole is the full window. The terminal fits the new size. The page remembers your choice for the next terminal you open. On a phone the panel is already full width, so the buttons are not shown.
|
|
58
|
+
|
|
47
59
|
The **Open the session** link in the sessions dock opens the same panel.
|
|
48
60
|
|
|
49
61
|
## Send a message to a session
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.163.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pmos-workspace",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.163.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -6,7 +6,21 @@
|
|
|
6
6
|
import { el } from './dom.js';
|
|
7
7
|
import { openSheet } from './c3-sheet.js';
|
|
8
8
|
import { toast } from './c3-toast.js';
|
|
9
|
-
import { pmGet } from './http.js';
|
|
9
|
+
import { pmGet, pmPost } from './http.js';
|
|
10
|
+
import { button } from './primitives.js';
|
|
11
|
+
import { c3ic } from './pm-work-view.js';
|
|
12
|
+
|
|
13
|
+
/* a primitives button (keyboard, role) carrying the console3 look and children (the pattern c3-dock.js uses) */
|
|
14
|
+
const c3btn = (attrs, onClick, ...kids) => { const b = button({ variant: 'ghost', attrs, onClick }); b.replaceChildren(...kids); return b; };
|
|
15
|
+
const store = {
|
|
16
|
+
get(k) { try { return localStorage.getItem(k); } catch { return null; } },
|
|
17
|
+
set(k, v) { try { localStorage.setItem(k, v); } catch { /* no storage */ } },
|
|
18
|
+
};
|
|
19
|
+
export const SIZE_KEY = 'pmos.workspace.c3.term-size';
|
|
20
|
+
export const PAD_KEY = 'pmos.workspace.c3.term-pad';
|
|
21
|
+
/* the sheet widths: Normal is the 600px wide sheet; the others are classes in console3.css (desktop only) */
|
|
22
|
+
export const SIZES = [['normal', 'Normal', 'Normal'], ['half', 'Half', 'Half of the window'], ['two-thirds', '2/3', 'Two-thirds of the window'], ['whole', 'Whole', 'Whole window']];
|
|
23
|
+
export const sizeOf = (v) => (SIZES.some((x) => x[0] === v) ? v : 'normal');
|
|
10
24
|
|
|
11
25
|
const VENDOR = ['/vendor/xterm.js', '/vendor/addon-fit.js', '/vendor/addon-web-links.js'];
|
|
12
26
|
let vendor = null;
|
|
@@ -29,6 +43,43 @@ function loadVendor() {
|
|
|
29
43
|
return vendor;
|
|
30
44
|
}
|
|
31
45
|
|
|
46
|
+
/* the scratchpad under the terminal: the classic app's per-session pad (panel-tabs.js), kept through /api/scratchpad */
|
|
47
|
+
function makePad(id, term, tab) {
|
|
48
|
+
const url = '/api/scratchpad?session=' + encodeURIComponent(id);
|
|
49
|
+
let typed = false; let timer = null; let pending = null; let closed = false;
|
|
50
|
+
const flush = () => {
|
|
51
|
+
if (timer) { clearTimeout(timer); timer = null; }
|
|
52
|
+
if (pending === null) return;
|
|
53
|
+
const text = pending; pending = null;
|
|
54
|
+
pmPost('/api/scratchpad', { session: id, text }).catch(() => { /* the text stays in the box */ });
|
|
55
|
+
};
|
|
56
|
+
const ta = el('textarea', { class: 'c3t-pad-ta', rows: '4', placeholder: 'Scratchpad. One pad for each session. Saved on this machine.', 'aria-label': 'Scratchpad', 'data-c3-pad': 'text',
|
|
57
|
+
oninput: () => { typed = true; pending = ta.value; if (timer) clearTimeout(timer); timer = setTimeout(flush, 500); sync(); } });
|
|
58
|
+
const send = c3btn({ class: 'pill', 'data-c3-pad': 'send', title: 'Type the pad into this session. Enter is not pressed.' }, () => {
|
|
59
|
+
if (!ta.value.trim()) return;
|
|
60
|
+
if (!term.sendToTerm(tab, ta.value)) { toast({ text: 'The text was not sent. The terminal is not connected.', tone: 'error' }); return; }
|
|
61
|
+
if (typeof term.focusTerm === 'function') term.focusTerm(tab);
|
|
62
|
+
}, c3ic('out', 'sm'), 'Send to terminal');
|
|
63
|
+
const open0 = store.get(PAD_KEY) !== 'closed';
|
|
64
|
+
const box = el('div', { class: 'c3t-pad', 'data-c3-pad': 'box' });
|
|
65
|
+
const toggle = c3btn({ class: 'chip2 lk', 'data-c3-pad': 'toggle' }, () => { const on = box.dataset.open !== 'true'; store.set(PAD_KEY, on ? 'open' : 'closed'); set(on); },
|
|
66
|
+
c3ic('edit', 'sm'), 'Scratchpad');
|
|
67
|
+
function set(on) {
|
|
68
|
+
box.dataset.open = String(on);
|
|
69
|
+
toggle.setAttribute('aria-expanded', String(on));
|
|
70
|
+
toggle.title = on ? 'Hide the scratchpad. The text is kept.' : 'Show the scratchpad';
|
|
71
|
+
ta.hidden = !on;
|
|
72
|
+
requestAnimationFrame(() => term.refitVisible());
|
|
73
|
+
}
|
|
74
|
+
function sync() { send.disabled = !ta.value.trim(); }
|
|
75
|
+
box.append(el('div', { class: 'c3t-pad-h' }, toggle, send), ta);
|
|
76
|
+
set(open0); sync();
|
|
77
|
+
pmGet(url).then((r) => { if (!typed && r && typeof r.text === 'string') { ta.value = r.text; sync(); } }).catch(() => { /* an empty pad */ });
|
|
78
|
+
const onHide = () => flush();
|
|
79
|
+
window.addEventListener('pagehide', onHide);
|
|
80
|
+
return { box, ta, dispose() { if (closed) return; closed = true; window.removeEventListener('pagehide', onHide); flush(); } };
|
|
81
|
+
}
|
|
82
|
+
|
|
32
83
|
export async function openSessionTerm({ id, name } = {}) {
|
|
33
84
|
if (!id) return null;
|
|
34
85
|
if (!name) { const r = await pmGet('/api/sessions').catch(() => null); const x = r && Array.isArray(r.sessions) ? r.sessions.find((y) => y.id === id) : null; name = x ? x.name : id; }
|
|
@@ -36,8 +87,23 @@ export async function openSessionTerm({ id, name } = {}) {
|
|
|
36
87
|
if (!ok || !term) { toast({ text: 'The terminal could not be loaded on this page.', tone: 'error' }); return null; }
|
|
37
88
|
const tab = { id: 'c3-session:' + id, kind: 'term', sessionId: id, spawn: false, sess: id };
|
|
38
89
|
const host = el('div', { class: 'c3t-term', 'data-c3-session-term': id });
|
|
39
|
-
|
|
40
|
-
const
|
|
90
|
+
const pad = makePad(id, term, tab);
|
|
91
|
+
const wrap = el('div', { class: 'c3t-tw' }, host, pad.box);
|
|
92
|
+
// the size control: Normal is the 600px sheet, the others are the classic dock's presets. Hidden on a phone, where the sheet is full width.
|
|
93
|
+
let size = sizeOf(store.get(SIZE_KEY));
|
|
94
|
+
const sizeBtns = SIZES.map(([k, label, title]) => c3btn({ 'aria-pressed': String(k === size), 'data-c3-size': k, title, 'aria-label': title }, () => setSize(k), label));
|
|
95
|
+
const seg = el('div', { class: 'seg c3t-size', role: 'group', 'aria-label': 'Terminal size', 'data-c3-size-seg': '' }, ...sizeBtns);
|
|
96
|
+
const cls = (k) => 'tsz-' + k;
|
|
97
|
+
// Esc and Tab belong to the shell while the terminal has focus (Esc interrupts an agent); the close button and a press outside still close.
|
|
98
|
+
// The scratchpad is outside the terminal host, so its keys are the page's own.
|
|
99
|
+
const sheet = openSheet({ title: name || id, icon: 'term', wide: true, body: wrap, actions: seg, cls: cls(size),
|
|
100
|
+
onClose: () => { pad.dispose(); term.disposeTerm(tab); }, passKeys: (e) => host.contains(e.target) });
|
|
101
|
+
function setSize(k) {
|
|
102
|
+
sheet.sheet.classList.remove(cls(size)); size = sizeOf(k); sheet.sheet.classList.add(cls(size));
|
|
103
|
+
store.set(SIZE_KEY, size);
|
|
104
|
+
sizeBtns.forEach((b) => b.setAttribute('aria-pressed', String(b.dataset.c3Size === size)));
|
|
105
|
+
requestAnimationFrame(() => { term.refitVisible(); requestAnimationFrame(() => term.refitVisible()); });
|
|
106
|
+
}
|
|
41
107
|
term.mountTerm(host, tab);
|
|
42
108
|
requestAnimationFrame(() => { term.refitVisible(); term.focusTerm(tab); });
|
|
43
109
|
return sheet;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* pm/c3-sheet.js — the console3 sheet, as a primitive (console3.html: bsHtml line 2502, the .sheet rules 143 to 145, 180, 496 to 502,
|
|
2
2
|
* the Esc handler 2536 and the phone drag 2538 to 2540).
|
|
3
3
|
*
|
|
4
|
-
* openSheet({ title, icon, body, wide, dw, scrim, onClose, passKeys }) -> { close, root, sheet, body } passKeys(e): true leaves that key to the page
|
|
4
|
+
* openSheet({ title, icon, body, wide, dw, scrim, onClose, passKeys, actions, cls }) -> { close, root, sheet, body } passKeys(e): true leaves that key to the page
|
|
5
|
+
* actions: a Node or an array of Nodes placed in the header before the close button; cls: extra class names on the sheet
|
|
5
6
|
* closeSheet() closes the open sheet, if any
|
|
6
7
|
*
|
|
7
8
|
* On a desktop window the sheet is the right-hand panel (340px; `wide` 600px; `dw` the document sheet, up to 780px) over a
|
|
@@ -50,7 +51,7 @@ export function closeSheet() {
|
|
|
50
51
|
if (typeof c.onClose === 'function') { try { c.onClose(); } catch { /* the caller reports its own errors */ } }
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
export function openSheet({ title = '', icon = 'doc', body, wide = false, dw = false, scrim = true, onClose, passKeys } = {}) {
|
|
54
|
+
export function openSheet({ title = '', icon = 'doc', body, wide = false, dw = false, scrim = true, onClose, passKeys, actions, cls = '' } = {}) {
|
|
54
55
|
closeSheet();
|
|
55
56
|
ensureC3Css();
|
|
56
57
|
const opener = document.activeElement && document.activeElement !== document.body ? document.activeElement : null;
|
|
@@ -60,8 +61,8 @@ export function openSheet({ title = '', icon = 'doc', body, wide = false, dw = f
|
|
|
60
61
|
const sbb = el('div', { class: 'brief sbb' }, ...bodyNodes(body));
|
|
61
62
|
const closeBtn = el('button', { type: 'button', class: 'ib sm', 'aria-label': 'Close', title: 'Close (Esc)', onclick: close }, c3ic('x', 'sm'));
|
|
62
63
|
const grab = el('div', { class: 'grab' });
|
|
63
|
-
const head = el('div', { class: 'sh2' }, el('span', { class: 'chips2' }, c3ic(icon), el('b', null, title)), el('span', { class: 'chips2' }, closeBtn));
|
|
64
|
-
const sheet = el('div', { class: 'sheet bs' + (wide ? ' wide' : '') + (dw ? ' dw' : ''), role: 'dialog', 'aria-modal': String(!!scrim), 'aria-label': title, tabindex: '-1' },
|
|
64
|
+
const head = el('div', { class: 'sh2' }, el('span', { class: 'chips2' }, c3ic(icon), el('b', null, title)), el('span', { class: 'chips2' }, ...bodyNodes(actions), closeBtn));
|
|
65
|
+
const sheet = el('div', { class: 'sheet bs' + (wide ? ' wide' : '') + (dw ? ' dw' : '') + (cls ? ' ' + cls : ''), role: 'dialog', 'aria-modal': String(!!scrim), 'aria-label': title, tabindex: '-1' },
|
|
65
66
|
grab, head, el('div', { class: 'sb' }, sbb));
|
|
66
67
|
/* the mockup's person and playbooks sheets have no scrim (they are panels beside the page, closed by their own button or Esc): `scrim: false` */
|
|
67
68
|
root.append(...(scrim ? [el('div', { class: 'scrim', onclick: close })] : []), sheet);
|
|
@@ -1660,6 +1660,22 @@
|
|
|
1660
1660
|
.c3 .c3t-panel{display:grid;gap:var(--sp-2);border:1px solid var(--border);border-radius:10px;padding:var(--sp-3)}
|
|
1661
1661
|
/* a session's terminal in a sheet (c3-session-term.js) */
|
|
1662
1662
|
.c3 .c3t-term{height:calc(100vh - 150px);min-height:240px;display:flex;flex-direction:column}
|
|
1663
|
+
/* the terminal plus its scratchpad (c3-session-term.js): the terminal shrinks to make room for the pad */
|
|
1664
|
+
.c3 .c3t-tw{height:calc(100vh - 150px);min-height:240px;display:flex;flex-direction:column;gap:var(--sp-2);min-width:0}
|
|
1665
|
+
.c3 .c3t-tw .c3t-term{height:auto;flex:1 1 0;min-height:120px}
|
|
1666
|
+
.c3 .c3t-pad{display:grid;gap:var(--sp-2);flex:none;border-top:1px solid var(--border);padding-top:var(--sp-2)}
|
|
1667
|
+
.c3 .c3t-pad-h{display:flex;gap:var(--sp-2);align-items:center;justify-content:space-between}
|
|
1668
|
+
.c3 .c3t-pad-h .pill:disabled{opacity:.35;cursor:not-allowed}
|
|
1669
|
+
.c3 .c3t-pad-ta{width:100%;min-height:72px;max-height:40vh;resize:vertical;font:var(--fs-2) var(--mono);box-sizing:border-box}
|
|
1670
|
+
/* terminal sheet sizes, as the classic dock has them; the phone sheet is already full width */
|
|
1671
|
+
.c3 .c3t-size{flex:none}
|
|
1672
|
+
.c3 .c3t-size button{padding:4px 8px}
|
|
1673
|
+
.c3.ph .c3t-size{display:none}
|
|
1674
|
+
@media (min-width:761px){
|
|
1675
|
+
.c3 .sheet.wide.tsz-half{width:50vw}
|
|
1676
|
+
.c3 .sheet.wide.tsz-two-thirds{width:66.667vw}
|
|
1677
|
+
.c3 .sheet.wide.tsz-whole{width:calc(100vw - 24px)}
|
|
1678
|
+
}
|
|
1663
1679
|
.c3 .c3t-panel .feed{display:flex;flex-direction:column;gap:var(--sp-2);max-height:220px;overflow:auto}
|
|
1664
1680
|
.c3 .c3t-panel .m{padding:6px 12px;border-radius:12px;max-width:88%;overflow-wrap:anywhere}
|
|
1665
1681
|
.c3 .c3t-panel .m.me{align-self:flex-end;background:var(--acc);color:var(--on-accent)}.c3 .c3t-panel .m.bot{background:var(--muted)}
|