@nanocollective/roster 0.1.0-alpha.16 → 0.1.0-alpha.17
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
CHANGED
|
@@ -109,8 +109,9 @@ main{padding:36px 44px 96px;max-width:1220px;overflow-x:hidden}
|
|
|
109
109
|
.thit .hitline em{font-style:normal;color:var(--ink);background:color-mix(in srgb,var(--accent) 22%,transparent);
|
|
110
110
|
border-radius:3px;padding:0 2px}
|
|
111
111
|
|
|
112
|
+
/* No height cap: a file grows the page, so there is one scrollbar, not one inside another. */
|
|
112
113
|
.viewer{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:22px 26px;
|
|
113
|
-
min-height:340px;
|
|
114
|
+
min-height:340px;min-width:0;box-shadow:var(--shadow-sm)}
|
|
114
115
|
.viewer pre{margin:0;white-space:pre-wrap;word-break:break-word;font:12.5px/1.65 var(--mono);color:var(--ink-dim)}
|
|
115
116
|
/* Source keeps its own line breaks and scrolls, rather than wrapping into soup. */
|
|
116
117
|
.viewer pre.code{white-space:pre;word-break:normal;overflow-x:auto}
|
|
@@ -14,11 +14,8 @@
|
|
|
14
14
|
* wrong. The server validates it before writing.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { askYes } from "../dialog.js";
|
|
20
|
-
import { el, esc, grow, kb, skeleton } from "../dom.js";
|
|
21
|
-
import { icon } from "../icons.js";
|
|
17
|
+
import { getFile, getOrgLayer } from "../api.js";
|
|
18
|
+
import { el, esc, kb, skeleton } from "../dom.js";
|
|
22
19
|
import { mdlite } from "../md.js";
|
|
23
20
|
import { humansOf, S, writeHash } from "../state.js";
|
|
24
21
|
import { yamlPre } from "../yaml.js";
|
|
@@ -166,112 +163,25 @@ export function viewOrg(m) {
|
|
|
166
163
|
viewer.replaceChildren();
|
|
167
164
|
const head = el("div", { className: "row filehead" });
|
|
168
165
|
head.append(el("span", { className: "meta", textContent: full(path) + " · " + kb(text.length) }));
|
|
169
|
-
/* The edit control used to be a word in a row of grey text, which is why "there is no easy
|
|
170
|
-
way to edit the org docs" was a fair thing to say about a screen that could edit them. */
|
|
171
166
|
const file = S.orgFiles.find((f) => f.path === path) ?? { path };
|
|
172
167
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
viewer.append(head, el("p", { className: "factsub", textContent: why(file) }), yamlPre(text));
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/* Prose files open on Read, with the ways to change them as tabs beside it: the same ones
|
|
182
|
-
Getting started offers, because a file is no easier to write after setup than during it. */
|
|
168
|
+
const isYaml = /\.ya?ml$/.test(path);
|
|
169
|
+
/* Every file opens on Read, with the ways to change it as tabs beside it: the same ones
|
|
170
|
+
Getting started offers, because a file is no easier to write after setup than during it.
|
|
171
|
+
org.yaml is checked by the server before it is written, so a bad one is refused. */
|
|
183
172
|
viewer.append(head, el("p", { className: "factsub", textContent: why(file) }));
|
|
184
|
-
const read = {
|
|
173
|
+
const read = {
|
|
174
|
+
label: "Read",
|
|
175
|
+
node: isYaml ? yamlPre(text) : el("div", { className: "md doc", innerHTML: mdlite(text) }),
|
|
176
|
+
};
|
|
185
177
|
const o = { path: full(path), name: S.data.name, text, before: [read], onSaved: () => show(path) };
|
|
186
178
|
viewer.append(
|
|
187
179
|
path === "org/business.md"
|
|
188
180
|
? businessForm(o)
|
|
189
181
|
: path === "org/priorities.md"
|
|
190
182
|
? prioritiesForm(o)
|
|
191
|
-
: fileModes(o),
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function editor(path, text) {
|
|
196
|
-
viewer.replaceChildren();
|
|
197
|
-
const isYaml = /\.ya?ml$/.test(path);
|
|
198
|
-
|
|
199
|
-
const status = el("span", { className: "meta" });
|
|
200
|
-
const save = el("button", { className: "ghbtn primary", textContent: "Save and commit" });
|
|
201
|
-
const cancel = el("button", { className: "ghbtn", textContent: "Cancel" });
|
|
202
|
-
|
|
203
|
-
const bar = el("div", { className: "row filehead" });
|
|
204
|
-
bar.append(el("span", { className: "meta", textContent: full(path) }), save, cancel, status);
|
|
205
|
-
viewer.append(bar);
|
|
206
|
-
|
|
207
|
-
const ta = el("textarea", { value: text, className: "editor" });
|
|
208
|
-
const hl = highlighted(ta, isYaml ? "yaml" : "md");
|
|
209
|
-
viewer.append(hl);
|
|
210
|
-
grow(ta);
|
|
211
|
-
hl.repaint();
|
|
212
|
-
ta.addEventListener("input", () => grow(ta));
|
|
213
|
-
// ⌘S is what a person's hands do in a text box. Without it, saving means finding a button
|
|
214
|
-
// above a screenful of textarea you have just scrolled past.
|
|
215
|
-
ta.addEventListener("keydown", (e) => {
|
|
216
|
-
if ((e.metaKey || e.ctrlKey) && e.key === "s") {
|
|
217
|
-
e.preventDefault();
|
|
218
|
-
commit();
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
ta.focus?.();
|
|
222
|
-
|
|
223
|
-
cancel.onclick = () => render(path, text);
|
|
224
|
-
save.onclick = commit;
|
|
225
|
-
|
|
226
|
-
viewer.append(
|
|
227
|
-
el("p", {
|
|
228
|
-
className: "editnote",
|
|
229
|
-
textContent:
|
|
230
|
-
"⌘S saves. Commits to " + S.data.opsName + " and pushes, and every staff member picks " +
|
|
231
|
-
"it up on their next run." +
|
|
232
|
-
(isYaml
|
|
233
|
-
? " org.yaml is checked before it is written: bad YAML, or a missing org or name, is refused rather than committed."
|
|
234
|
-
: ""),
|
|
235
|
-
}),
|
|
183
|
+
: fileModes({ ...o, lang: isYaml ? "yaml" : "md" }),
|
|
236
184
|
);
|
|
237
|
-
|
|
238
|
-
async function commit() {
|
|
239
|
-
if (ta.value === text) {
|
|
240
|
-
status.textContent = "nothing changed";
|
|
241
|
-
status.className = "meta";
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
/* Only the manifest asks. A prose edit is one commit to revert and the agents pick it up
|
|
245
|
-
on their next run; org.yaml is the file that can stop every prompt composing, and the
|
|
246
|
-
portal has just told you it validates it. Asking every time taught people to click
|
|
247
|
-
through the question, which is worse than not asking. */
|
|
248
|
-
if (
|
|
249
|
-
isYaml &&
|
|
250
|
-
!(await askYes({
|
|
251
|
-
title: "Commit " + full(path) + " and push?",
|
|
252
|
-
hint: "Every staff member picks this up on their next run.",
|
|
253
|
-
confirm: "Commit and push",
|
|
254
|
-
}))
|
|
255
|
-
) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
save.disabled = cancel.disabled = true;
|
|
259
|
-
status.textContent = "committing…";
|
|
260
|
-
status.className = "meta";
|
|
261
|
-
try {
|
|
262
|
-
const r = await post({ path: full(path), text: ta.value }, "/api/save");
|
|
263
|
-
status.textContent = r.pushed
|
|
264
|
-
? "committed and pushed · " + r.sha
|
|
265
|
-
: "committed " + r.sha + ", but the push failed: " + (r.note ?? "");
|
|
266
|
-
status.className = r.pushed ? "meta ok" : "meta warn";
|
|
267
|
-
render(path, ta.value);
|
|
268
|
-
} catch (e) {
|
|
269
|
-
// A refused org.yaml is the interesting case: the reason is the whole message.
|
|
270
|
-
status.textContent = e.message;
|
|
271
|
-
status.className = "meta err";
|
|
272
|
-
save.disabled = cancel.disabled = false;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
185
|
}
|
|
276
186
|
}
|
|
277
187
|
|