create-pmos 0.15.0 → 0.16.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/README.md +38 -0
- package/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/workspace/README.md +44 -12
- package/workspace/design-parity.js +358 -0
- package/workspace/lib/find-browser.js +60 -0
- package/workspace/lib/four-panel.js +345 -0
- package/workspace/lib/git-info.js +28 -1
- package/workspace/lib/mobile-nav.js +32 -2
- package/workspace/package-lock.json +29 -2
- package/workspace/package.json +13 -5
- package/workspace/public/index.html +79 -409
- package/workspace/public/pm/app.js +242 -0
- package/workspace/public/pm/data.js +877 -0
- package/workspace/public/pm/dom.js +116 -0
- package/workspace/public/pm/fixture.js +979 -0
- package/workspace/public/pm/mobile.css +200 -0
- package/workspace/public/pm/mobile.js +403 -0
- package/workspace/public/pm/modes.js +1076 -0
- package/workspace/public/pm/panel-detail.js +648 -0
- package/workspace/public/pm/panel-initiatives.js +426 -0
- package/workspace/public/pm/panel-repos.js +460 -0
- package/workspace/public/pm/panel-tabs.js +487 -0
- package/workspace/public/pm/state.js +164 -0
- package/workspace/public/pm/tab-doc.js +279 -0
- package/workspace/public/pm/tab-task.js +83 -0
- package/workspace/public/pm/tab-term.js +560 -0
- package/workspace/public/pm/tokens.css +73 -0
- package/workspace/public/vendor/ibm-plex-mono-500.woff2 +0 -0
- package/workspace/server.js +158 -8
- package/workspace/focus.test.js +0 -650
- package/workspace/handoff.test.js +0 -443
- package/workspace/profiles.test.js +0 -362
- package/workspace/public/app.js +0 -5560
- package/workspace/public/styles.css +0 -2258
- package/workspace/repo-scan.test.js +0 -327
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* pm/dom.js — the whole DOM layer for the four-panel workspace.
|
|
2
|
+
*
|
|
3
|
+
* The design prototype is a React tree whose every element carries a hand-written inline `style`
|
|
4
|
+
* string plus an optional `style-hover`. This surface is a PORT of that tree, so the helper is
|
|
5
|
+
* built to make a port a copy-paste: `attrs.style` takes the prototype's style string verbatim and
|
|
6
|
+
* `attrs.hover` takes its style-hover string verbatim. Nothing is parsed, normalised or
|
|
7
|
+
* re-formatted — a value that survives the copy unchanged cannot drift from the design.
|
|
8
|
+
*
|
|
9
|
+
* el('button', { style: 'display:flex;gap:8px', hover: 'background:var(--accent)',
|
|
10
|
+
* onClick: fn, title: 'x' }, span, 'text')
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Append `kids` (nodes, strings, numbers, arrays, null/false) to `node`. */
|
|
14
|
+
function append(node, kids) {
|
|
15
|
+
for (const k of kids) {
|
|
16
|
+
if (k === null || k === undefined || k === false || k === true) continue;
|
|
17
|
+
if (Array.isArray(k)) { append(node, k); continue; }
|
|
18
|
+
node.appendChild(k instanceof Node ? k : document.createTextNode(String(k)));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Reproduce the prototype's `style-hover`: on mouseenter the declarations are appended to the
|
|
24
|
+
* element's own inline style (so, being last, they win for any property they repeat); on
|
|
25
|
+
* mouseleave the original attribute is restored exactly.
|
|
26
|
+
*/
|
|
27
|
+
export function hover(node, decls) {
|
|
28
|
+
if (!decls) return node;
|
|
29
|
+
let saved = null;
|
|
30
|
+
node.addEventListener('mouseenter', () => {
|
|
31
|
+
if (saved !== null) return;
|
|
32
|
+
saved = node.getAttribute('style') || '';
|
|
33
|
+
const sep = saved && !saved.trimEnd().endsWith(';') ? ';' : '';
|
|
34
|
+
node.setAttribute('style', saved + sep + decls);
|
|
35
|
+
});
|
|
36
|
+
node.addEventListener('mouseleave', () => {
|
|
37
|
+
if (saved === null) return;
|
|
38
|
+
if (saved) node.setAttribute('style', saved); else node.removeAttribute('style');
|
|
39
|
+
saved = null;
|
|
40
|
+
});
|
|
41
|
+
return node;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Create an element. See the module header for the attribute contract. */
|
|
45
|
+
export function el(tag, attrs, ...children) {
|
|
46
|
+
const node = document.createElement(tag);
|
|
47
|
+
if (attrs) {
|
|
48
|
+
for (const k in attrs) {
|
|
49
|
+
const v = attrs[k];
|
|
50
|
+
if (v === null || v === undefined || v === false) continue;
|
|
51
|
+
if (k === 'style') node.setAttribute('style', typeof v === 'string' ? v : style(v));
|
|
52
|
+
else if (k === 'hover' || k === 'styleHover') hover(node, v);
|
|
53
|
+
else if (k === 'ref') { if (typeof v === 'function') v(node); }
|
|
54
|
+
else if (k === 'value' || k === 'checked' || k === 'disabled' || k === 'selected') node[k] = v;
|
|
55
|
+
else if (k === 'html') node.innerHTML = String(v);
|
|
56
|
+
else if (k.length > 2 && k[0] === 'o' && k[1] === 'n' && typeof v === 'function') {
|
|
57
|
+
node.addEventListener(k.slice(2).toLowerCase(), v);
|
|
58
|
+
} else if (v === true) node.setAttribute(k, '');
|
|
59
|
+
else node.setAttribute(k, String(v));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
append(node, children);
|
|
63
|
+
return node;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** {backgroundColor:'red', minHeight:'30px'} -> 'background-color:red;min-height:30px'. */
|
|
67
|
+
export function style(obj) {
|
|
68
|
+
if (typeof obj === 'string') return obj;
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const k in obj) {
|
|
71
|
+
const v = obj[k];
|
|
72
|
+
if (v === null || v === undefined || v === false) continue;
|
|
73
|
+
out.push(k.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase()) + ':' + v);
|
|
74
|
+
}
|
|
75
|
+
return out.join(';');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Remove every child of `node`, returning it so it can be filled in the same expression. */
|
|
79
|
+
export function clear(node) {
|
|
80
|
+
while (node.firstChild) node.removeChild(node.firstChild);
|
|
81
|
+
return node;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A document fragment holding `kids` — for returning several siblings from one function. */
|
|
85
|
+
export function frag(...kids) {
|
|
86
|
+
const f = document.createDocumentFragment();
|
|
87
|
+
append(f, kids);
|
|
88
|
+
return f;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A text node. Named so a port reads `text(p.name)` where the prototype read `{{ p.name }}`. */
|
|
92
|
+
export function text(s) {
|
|
93
|
+
return document.createTextNode(s === null || s === undefined ? '' : String(s));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** addEventListener that returns its own undo — used where a listener outlives one render. */
|
|
97
|
+
export function on(node, ev, fn, opts) {
|
|
98
|
+
node.addEventListener(ev, fn, opts);
|
|
99
|
+
return () => node.removeEventListener(ev, fn, opts);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Reconcile `parent`'s children to exactly `kids`, in order, WITHOUT recreating nodes that are
|
|
104
|
+
* already there. The shell relies on this: panel 4 hosts a live xterm, and a terminal that is
|
|
105
|
+
* detached and re-attached on every unrelated state change loses its grid — the quiet PTY
|
|
106
|
+
* regression the PRD names. Nodes carried over keep their identity, listeners and scroll.
|
|
107
|
+
*/
|
|
108
|
+
export function setChildren(parent, kids) {
|
|
109
|
+
const want = kids.filter((k) => k);
|
|
110
|
+
const keep = new Set(want);
|
|
111
|
+
for (const n of [...parent.childNodes]) if (!keep.has(n)) parent.removeChild(n);
|
|
112
|
+
want.forEach((k, i) => {
|
|
113
|
+
if (parent.childNodes[i] !== k) parent.insertBefore(k, parent.childNodes[i] || null);
|
|
114
|
+
});
|
|
115
|
+
return parent;
|
|
116
|
+
}
|