@bongos/core 1.21.40 → 1.21.42
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/.bongos-core.json +136 -76
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +16 -0
- package/clients/bongos-client/index.cjs +16 -0
- package/clients/bongos-client/index.d.ts +25 -0
- package/clients/bongos-client/index.mjs +16 -0
- package/docs/api/openapi.json +492 -3
- package/docs/api-reference.md +13 -1
- package/docs/copy-inventory.md +29 -12
- package/docs/copy-registry.json +177 -14
- package/docs/file-map.md +1 -0
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +4 -2
- package/docs/page-readings.json +574 -547
- package/modules/autonomy/governor-docket.js +6 -1
- package/modules/hall-ui/public/atlas.html +3 -3
- package/modules/hall-ui/public/blockers.html +3 -3
- package/modules/hall-ui/public/board-room.html +3 -3
- package/modules/hall-ui/public/city.html +3 -3
- package/modules/hall-ui/public/collab.html +3 -3
- package/modules/hall-ui/public/commands.html +3 -3
- package/modules/hall-ui/public/copy-desk.html +3 -3
- package/modules/hall-ui/public/craft-mode.js +62 -0
- package/modules/hall-ui/public/deploy.html +3 -3
- package/modules/hall-ui/public/diagrams.html +3 -3
- package/modules/hall-ui/public/drachmae.html +3 -3
- package/modules/hall-ui/public/fleet.html +3 -3
- package/modules/hall-ui/public/gate.html +3 -3
- package/modules/hall-ui/public/goal-map.html +3 -3
- package/modules/hall-ui/public/goals.html +3 -3
- package/modules/hall-ui/public/government.html +3 -3
- package/modules/hall-ui/public/idea.html +3 -3
- package/modules/hall-ui/public/ideas.html +3 -3
- package/modules/hall-ui/public/index.html +3 -3
- package/modules/hall-ui/public/modules.html +3 -3
- package/modules/hall-ui/public/page-notes.js +320 -0
- package/modules/hall-ui/public/primer.html +3 -3
- package/modules/hall-ui/public/profile.html +3 -3
- package/modules/hall-ui/public/project-settings.html +3 -3
- package/modules/hall-ui/public/ranks.html +3 -3
- package/modules/hall-ui/public/roadmap.html +3 -3
- package/modules/hall-ui/public/roster.html +3 -3
- package/modules/hall-ui/public/sessions.html +3 -3
- package/modules/hall-ui/public/settings.html +3 -3
- package/modules/hall-ui/public/street-card.css +13 -0
- package/modules/hall-ui/public/street-card.js +61 -9
- package/modules/hall-ui/public/street-card.states.json +72 -0
- package/modules/hall-ui/public/street.html +3 -3
- package/modules/hall-ui/public/studio.html +3 -3
- package/modules/hall-ui/public/style.css +144 -0
- package/modules/hall-ui/public/task.html +3 -3
- package/modules/hall-ui/public/thinking.html +3 -3
- package/modules/hall-ui/public/tweak-editor.html +3 -3
- package/modules/hall-ui/public/watch.html +3 -3
- package/modules/hall-ui/public/work.html +3 -3
- package/modules/hall-ui/records/governor-street.md +1 -0
- package/modules/hall-ui/records/page-notes.md +16 -0
- package/modules/page-notes/CLAUDE.md +15 -0
- package/modules/page-notes/db.js +147 -0
- package/modules/page-notes/migrations/page_notes_001_notes.sql +68 -0
- package/modules/page-notes/module.json +13 -0
- package/modules/page-notes/notes.js +174 -0
- package/modules/page-notes/routes/page-notes.js +172 -0
- package/modules/page-notes/split.js +68 -0
- package/modules/provisioning/starter-bundles.js +1 -0
- package/modules/ui-design/kit/serve.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/gds/publish-manifest.js +1 -0
- package/src/bongos/module-scope-map.js +4 -0
- package/src/bongos/routes.js +4 -1
- package/src/module-api.js +1 -1
- package/tests/governor_docket.mjs +3 -1
- package/tests/hall_matter_card.mjs +71 -0
- package/tests/hall_tweak_editor.mjs +1 -1
- package/tests/module_loader.mjs +1 -1
- package/tests/page_notes.mjs +289 -0
- package/tests/page_notes_db.mjs +140 -0
- package/tests/page_notes_panel.mjs +145 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// page-notes.js — the Artistry palette's feedback doc (task 1004496, goal
|
|
2
|
+
// 1000095 WA6). Loaded on demand by craft-mode.js: when the palette button is
|
|
3
|
+
// pressed (open) or a page arrives with ?note=N (openNote). Never on page load.
|
|
4
|
+
//
|
|
5
|
+
// A side sheet over the page: write a doc about THIS page (as long as it needs
|
|
6
|
+
// to be), add reference images, and "Turn into ideas" — the server's AI split
|
|
7
|
+
// files each point as its own idea under your name, and the doc stays as the
|
|
8
|
+
// page's reference. Below the writing area, the page's earlier docs; each opens
|
|
9
|
+
// in place. The draft saves as you type, so closing loses nothing.
|
|
10
|
+
//
|
|
11
|
+
// The walls are the server's (modules/page-notes): a draft is its author's, a
|
|
12
|
+
// submitted doc is any signed-in builder's. This file only draws.
|
|
13
|
+
(function () {
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
var API = '/api/bongos/page-notes';
|
|
17
|
+
var IMAGE_RE = /!\[([^\]\n]{0,120})\]\(\/api\/bongos\/page-notes\/images\/(\d{1,18})\)/g;
|
|
18
|
+
var MAX_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
19
|
+
var MAX_IMAGES = 8;
|
|
20
|
+
var TYPES = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
|
|
21
|
+
|
|
22
|
+
var sheet = null, returnFocus = null, state = null, saveTimer = null, pollTimer = null;
|
|
23
|
+
|
|
24
|
+
function esc(s) {
|
|
25
|
+
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
|
26
|
+
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function page() {
|
|
30
|
+
var b = document.body || { dataset: {} };
|
|
31
|
+
var slug = String((b.dataset && b.dataset.page) || 'home').toLowerCase().replace(/[^a-z0-9_-]/g, '-').slice(0, 64) || 'home';
|
|
32
|
+
var title = (b.dataset && b.dataset.pageTitle) || String(document.title || '').split(/\s[—-]\s/)[0] || slug;
|
|
33
|
+
var path = String((window.location && window.location.pathname) || '/').replace(/[^A-Za-z0-9/_.-]/g, '').slice(0, 200) || '/';
|
|
34
|
+
return { id: 'builders:' + slug, path: path, title: title.slice(0, 120) };
|
|
35
|
+
}
|
|
36
|
+
function api(method, path, body) {
|
|
37
|
+
var opts = { method: method, credentials: 'same-origin', headers: {} };
|
|
38
|
+
if (body !== undefined) { opts.headers['content-type'] = 'application/json'; opts.body = JSON.stringify(body); }
|
|
39
|
+
return fetch(API + path, opts).then(function (r) {
|
|
40
|
+
return r.json().catch(function () { return {}; }).then(function (j) {
|
|
41
|
+
if (!r.ok) {
|
|
42
|
+
var e = new Error((j && j.error && j.error.message) || (r.status === 401 ? 'Sign in to write page feedback.' : 'That did not work. Try again in a moment.'));
|
|
43
|
+
e.status = r.status;
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
return j;
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function day(iso) {
|
|
51
|
+
var d = iso ? new Date(iso) : null;
|
|
52
|
+
return d && !isNaN(d) ? d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) : '';
|
|
53
|
+
}
|
|
54
|
+
function plural(n, one, many) { return n + ' ' + (n === 1 ? one : many); }
|
|
55
|
+
|
|
56
|
+
// The doc, read: headings, lists, paragraphs, and only this module's own images.
|
|
57
|
+
function renderBody(md) {
|
|
58
|
+
var out = [], para = [], list = [];
|
|
59
|
+
function flushPara() { if (para.length) { out.push('<p>' + para.join('<br>') + '</p>'); para = []; } }
|
|
60
|
+
function flushList() { if (list.length) { out.push('<ul>' + list.map(function (x) { return '<li>' + x + '</li>'; }).join('') + '</ul>'); list = []; } }
|
|
61
|
+
function inline(line) {
|
|
62
|
+
return esc(line).replace(IMAGE_RE, function (_m, alt, id) {
|
|
63
|
+
return '<img class="pn-doc__img" src="' + API + '/images/' + id + '" alt="' + alt + '" loading="lazy">';
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
String(md || '').split('\n').forEach(function (raw) {
|
|
67
|
+
var line = raw.replace(/\s+$/, '');
|
|
68
|
+
var h = /^(#{1,3})\s+(.*)$/.exec(line);
|
|
69
|
+
var li = /^\s*[-*]\s+(.*)$/.exec(line);
|
|
70
|
+
if (!line.trim()) { flushPara(); flushList(); return; }
|
|
71
|
+
if (h) { flushPara(); flushList(); out.push('<h3 class="pn-doc__h">' + inline(h[2]) + '</h3>'); return; }
|
|
72
|
+
if (li) { flushPara(); list.push(inline(li[1])); return; }
|
|
73
|
+
flushList();
|
|
74
|
+
para.push(inline(line));
|
|
75
|
+
});
|
|
76
|
+
flushPara(); flushList();
|
|
77
|
+
return out.join('');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------- the sheet ----------
|
|
81
|
+
function shellHtml(p) {
|
|
82
|
+
return '<div class="pn__panel" role="dialog" aria-modal="true" aria-labelledby="pn-title">' +
|
|
83
|
+
'<div class="pn__head">' +
|
|
84
|
+
'<p class="pn__eyebrow">Page feedback · ' + esc(p.title) + '</p>' +
|
|
85
|
+
'<h2 class="pn__title" id="pn-title">What should this page be?</h2>' +
|
|
86
|
+
'<button class="pn__close" type="button" data-pn-close aria-label="Close">' +
|
|
87
|
+
'<svg viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M5 5l8 8M13 5l-8 8"/></svg></button>' +
|
|
88
|
+
'</div>' +
|
|
89
|
+
'<div class="pn__body" id="pn-body"><p class="pn__quiet">Opening your doc…</p></div>' +
|
|
90
|
+
'</div>';
|
|
91
|
+
}
|
|
92
|
+
function writeHtml() {
|
|
93
|
+
var n = state.note, imgs = state.images;
|
|
94
|
+
return '<label class="pn__label" for="pn-text">Your feedback</label>' +
|
|
95
|
+
'<textarea class="pn__text" id="pn-text" rows="14" placeholder="Write what you see on this page and what it should be. Long is fine: each separate point becomes its own idea.">' + esc(n.body_md) + '</textarea>' +
|
|
96
|
+
'<div class="pn__tools">' +
|
|
97
|
+
'<label class="pn__add"><input type="file" id="pn-file" accept="' + TYPES.join(',') + '" multiple>Add images</label>' +
|
|
98
|
+
'<span class="pn__quiet" id="pn-count">' + imgs.length + ' of ' + MAX_IMAGES + ' images</span>' +
|
|
99
|
+
'<span class="pn__quiet pn__saved" id="pn-saved" aria-live="polite"></span>' +
|
|
100
|
+
'</div>' +
|
|
101
|
+
(imgs.length ? '<div class="pn__thumbs">' + imgs.map(function (i) {
|
|
102
|
+
return '<img class="pn__thumb" src="' + esc(i.url) + '" alt="' + esc(i.name || 'reference image') + '">';
|
|
103
|
+
}).join('') + '</div>' : '') +
|
|
104
|
+
'<p class="pn__error" id="pn-error" role="alert"></p>' +
|
|
105
|
+
'<div class="pn__foot">' +
|
|
106
|
+
'<p class="pn__hint">When you submit, an AI splits this into separate ideas, filed under your name. The doc stays here as this page\'s reference.</p>' +
|
|
107
|
+
'<button class="pn__submit" type="button" data-pn-submit>Turn into ideas</button>' +
|
|
108
|
+
'</div>' +
|
|
109
|
+
listHtml();
|
|
110
|
+
}
|
|
111
|
+
function listHtml() {
|
|
112
|
+
var rows = state.list || [];
|
|
113
|
+
return '<section class="pn__list" aria-labelledby="pn-list-h"><h3 class="pn__list-h" id="pn-list-h">This page\'s reference docs</h3>' +
|
|
114
|
+
(rows.length ? '<ul class="pn__rows">' + rows.map(function (r) {
|
|
115
|
+
return '<li><button class="pn__row" type="button" data-pn-note="' + esc(r.id) + '">' +
|
|
116
|
+
'<span class="pn__row-meta">' + esc(r.author_name) + ' · ' + esc(day(r.submitted_at)) + ' · ' +
|
|
117
|
+
(r.status === 'filed' ? esc(plural(r.idea_count, 'idea', 'ideas')) : 'splitting…') + '</span>' +
|
|
118
|
+
'<span class="pn__row-text">' + esc(String(r.excerpt || '').replace(IMAGE_RE, '[image]').replace(/\s+/g, ' ').slice(0, 160)) + '</span>' +
|
|
119
|
+
'</button></li>';
|
|
120
|
+
}).join('') + '</ul>' : '<p class="pn__quiet">None yet. Yours will be the first.</p>') +
|
|
121
|
+
'</section>';
|
|
122
|
+
}
|
|
123
|
+
function ideasHtml(ideas) {
|
|
124
|
+
if (!ideas || !ideas.length) return '';
|
|
125
|
+
return '<ul class="pn__ideas">' + ideas.map(function (i) {
|
|
126
|
+
return '<li><a href="/#/idea/' + esc(i.id) + '">' + esc(i.title) + '</a></li>';
|
|
127
|
+
}).join('') + '</ul>';
|
|
128
|
+
}
|
|
129
|
+
function readHtml(doc) {
|
|
130
|
+
var n = doc.note;
|
|
131
|
+
var filed = n.status === 'filed';
|
|
132
|
+
return '<button class="pn__back" type="button" data-pn-back>← ' + (state && state.note ? 'Back to your doc' : 'This page\'s docs') + '</button>' +
|
|
133
|
+
'<p class="pn__row-meta">' + esc(n.author_name) + ' · ' + esc(day(n.submitted_at)) + ' · ' + esc(n.page_title || n.page_id) + '</p>' +
|
|
134
|
+
'<div class="pn-doc">' + renderBody(n.body_md) + '</div>' +
|
|
135
|
+
(filed
|
|
136
|
+
? '<h3 class="pn__list-h">It became ' + esc(plural(doc.ideas.length, 'idea', 'ideas')) + '</h3>' + ideasHtml(doc.ideas)
|
|
137
|
+
: '<p class="pn__quiet">Being split into ideas…</p>');
|
|
138
|
+
}
|
|
139
|
+
function doneHtml(doc) {
|
|
140
|
+
return '<p class="pn__done">Filed as ' + esc(plural(doc.ideas.length, 'idea', 'ideas')) + ' under your name.</p>' +
|
|
141
|
+
ideasHtml(doc.ideas) +
|
|
142
|
+
'<p class="pn__hint">The doc is now this page\'s reference. Every idea links back to it.</p>' +
|
|
143
|
+
'<button class="pn__back" type="button" data-pn-new>Write another</button>';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function body() { return sheet && sheet.querySelector('#pn-body'); }
|
|
147
|
+
function setError(msg) { var e = sheet && sheet.querySelector('#pn-error'); if (e) e.textContent = msg || ''; }
|
|
148
|
+
function setSaved(msg) { var e = sheet && sheet.querySelector('#pn-saved'); if (e) e.textContent = msg || ''; }
|
|
149
|
+
|
|
150
|
+
function setTitle(text) { var h = sheet && sheet.querySelector('#pn-title'); if (h) h.textContent = text; }
|
|
151
|
+
function showWrite() {
|
|
152
|
+
var b = body();
|
|
153
|
+
if (!b) return;
|
|
154
|
+
setTitle('What should this page be?');
|
|
155
|
+
b.innerHTML = writeHtml();
|
|
156
|
+
var t = b.querySelector('#pn-text');
|
|
157
|
+
if (t) { t.addEventListener('input', queueSave); t.focus(); }
|
|
158
|
+
var f = b.querySelector('#pn-file');
|
|
159
|
+
if (f) f.addEventListener('change', function () { addImages(Array.prototype.slice.call(f.files || [])); f.value = ''; });
|
|
160
|
+
}
|
|
161
|
+
function currentText() { var t = sheet && sheet.querySelector('#pn-text'); return t ? t.value : (state && state.note ? state.note.body_md : ''); }
|
|
162
|
+
function queueSave() {
|
|
163
|
+
setSaved('');
|
|
164
|
+
if (saveTimer) clearTimeout(saveTimer);
|
|
165
|
+
saveTimer = setTimeout(save, 700);
|
|
166
|
+
}
|
|
167
|
+
function save() {
|
|
168
|
+
if (saveTimer) { clearTimeout(saveTimer); saveTimer = null; }
|
|
169
|
+
if (!state || !state.note || state.note.status !== 'draft') return Promise.resolve();
|
|
170
|
+
var text = currentText();
|
|
171
|
+
if (text === state.note.body_md) return Promise.resolve();
|
|
172
|
+
return api('PATCH', '/' + state.note.id, { body_md: text }).then(function () {
|
|
173
|
+
state.note.body_md = text;
|
|
174
|
+
setSaved('Saved');
|
|
175
|
+
}, function (e) { setError(e.message); });
|
|
176
|
+
}
|
|
177
|
+
function readFile(file) {
|
|
178
|
+
return new Promise(function (resolve, reject) {
|
|
179
|
+
var r = new FileReader();
|
|
180
|
+
r.onload = function () { resolve(String(r.result || '')); };
|
|
181
|
+
r.onerror = function () { reject(new Error('That file could not be read.')); };
|
|
182
|
+
r.readAsDataURL(file);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function addImages(files) {
|
|
186
|
+
setError('');
|
|
187
|
+
var chain = Promise.resolve();
|
|
188
|
+
files.forEach(function (file) {
|
|
189
|
+
chain = chain.then(function () {
|
|
190
|
+
if (TYPES.indexOf(file.type) === -1) throw new Error('Images only: PNG, JPG, GIF or WebP.');
|
|
191
|
+
if (file.size > MAX_IMAGE_BYTES) throw new Error('Each image can be up to 4 MB.');
|
|
192
|
+
if (state.images.length >= MAX_IMAGES) throw new Error('A doc can carry up to ' + MAX_IMAGES + ' images.');
|
|
193
|
+
return readFile(file).then(function (data) {
|
|
194
|
+
return api('POST', '/' + state.note.id + '/images', { content_type: file.type, data: data, name: String(file.name || '').slice(0, 120) });
|
|
195
|
+
}).then(function (j) {
|
|
196
|
+
state.images.push(j.image);
|
|
197
|
+
var name = String(j.image.name || 'image').replace(/[\]\[()]/g, '');
|
|
198
|
+
var t = sheet.querySelector('#pn-text');
|
|
199
|
+
var text = t ? t.value : '';
|
|
200
|
+
var at = t && typeof t.selectionStart === 'number' ? t.selectionStart : text.length;
|
|
201
|
+
var md = (at && text[at - 1] !== '\n' ? '\n' : '') + '\n';
|
|
202
|
+
state.note.body_md = text;
|
|
203
|
+
var next = text.slice(0, at) + md + text.slice(at);
|
|
204
|
+
return api('PATCH', '/' + state.note.id, { body_md: next }).then(function () {
|
|
205
|
+
state.note.body_md = next;
|
|
206
|
+
showWrite();
|
|
207
|
+
setSaved('Saved');
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
chain.catch(function (e) { setError(e.message); });
|
|
213
|
+
}
|
|
214
|
+
function submit() {
|
|
215
|
+
setError('');
|
|
216
|
+
var btn = sheet.querySelector('[data-pn-submit]');
|
|
217
|
+
if (btn) btn.disabled = true;
|
|
218
|
+
save().then(function () {
|
|
219
|
+
return api('POST', '/' + state.note.id + '/submit');
|
|
220
|
+
}).then(function () {
|
|
221
|
+
var b = body();
|
|
222
|
+
if (b) b.innerHTML = '<p class="pn__done" aria-live="polite">Splitting your feedback into ideas…</p>';
|
|
223
|
+
poll(state.note.id, 0);
|
|
224
|
+
}, function (e) { if (btn) btn.disabled = false; setError(e.message); });
|
|
225
|
+
}
|
|
226
|
+
function poll(id, tries) {
|
|
227
|
+
pollTimer = setTimeout(function () {
|
|
228
|
+
api('GET', '/' + id).then(function (doc) {
|
|
229
|
+
if (!sheet) return;
|
|
230
|
+
if (doc.note.status === 'filed') { state.note = null; body().innerHTML = doneHtml(doc); refreshList(); return; }
|
|
231
|
+
if (tries < 60) poll(id, tries + 1);
|
|
232
|
+
else body().innerHTML = '<p class="pn__quiet">Still splitting. It will appear in this page\'s docs when it is done.</p>';
|
|
233
|
+
}, function () { if (tries < 60) poll(id, tries + 1); });
|
|
234
|
+
}, tries ? 2000 : 1200);
|
|
235
|
+
}
|
|
236
|
+
function refreshList() {
|
|
237
|
+
return api('GET', '?page_id=' + encodeURIComponent(page().id)).then(function (j) { if (state) state.list = j.notes || []; }, function () { /* the list is a convenience */ });
|
|
238
|
+
}
|
|
239
|
+
function openDraft() {
|
|
240
|
+
var p = page();
|
|
241
|
+
return Promise.all([
|
|
242
|
+
api('POST', '', { page_id: p.id, page_path: p.path, page_title: p.title }),
|
|
243
|
+
refreshList(),
|
|
244
|
+
]).then(function (r) {
|
|
245
|
+
state.note = r[0].note;
|
|
246
|
+
state.images = r[0].images || [];
|
|
247
|
+
showWrite();
|
|
248
|
+
}, function (e) {
|
|
249
|
+
var b = body();
|
|
250
|
+
if (b) b.innerHTML = '<p class="pn__error" role="alert">' + esc(e.message) + '</p>';
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function showNote(id) {
|
|
254
|
+
var b = body();
|
|
255
|
+
setTitle('Feedback on this page');
|
|
256
|
+
if (b) b.innerHTML = '<p class="pn__quiet">Opening the doc…</p>';
|
|
257
|
+
return save().then(function () { return api('GET', '/' + id); }).then(function (doc) {
|
|
258
|
+
if (b) { b.innerHTML = readHtml(doc); var back = b.querySelector('[data-pn-back]'); if (back) back.focus(); }
|
|
259
|
+
}, function (e) {
|
|
260
|
+
if (b) b.innerHTML = '<p class="pn__error" role="alert">' + esc(e.message) + '</p><button class="pn__back" type="button" data-pn-back>← Back</button>';
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function onClick(e) {
|
|
265
|
+
if (e.target === sheet) { close(); return; }
|
|
266
|
+
var btn = e.target.closest && e.target.closest('button');
|
|
267
|
+
if (!btn) return;
|
|
268
|
+
if (btn.hasAttribute('data-pn-close')) close();
|
|
269
|
+
else if (btn.hasAttribute('data-pn-submit')) submit();
|
|
270
|
+
else if (btn.hasAttribute('data-pn-note')) showNote(btn.getAttribute('data-pn-note'));
|
|
271
|
+
else if (btn.hasAttribute('data-pn-back') || btn.hasAttribute('data-pn-new')) {
|
|
272
|
+
if (state.note && !btn.hasAttribute('data-pn-new')) showWrite();
|
|
273
|
+
else { body().innerHTML = '<p class="pn__quiet">Opening your doc…</p>'; openDraft(); }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function onKey(e) {
|
|
277
|
+
if (!sheet) return;
|
|
278
|
+
if (e.key === 'Escape') { e.preventDefault(); close(); return; }
|
|
279
|
+
if (e.key !== 'Tab') return;
|
|
280
|
+
var f = Array.prototype.slice.call(sheet.querySelectorAll('button, textarea, input, a[href]')).filter(function (x) { return !x.disabled; });
|
|
281
|
+
if (!f.length) return;
|
|
282
|
+
if (e.shiftKey && document.activeElement === f[0]) { e.preventDefault(); f[f.length - 1].focus(); }
|
|
283
|
+
else if (!e.shiftKey && document.activeElement === f[f.length - 1]) { e.preventDefault(); f[0].focus(); }
|
|
284
|
+
}
|
|
285
|
+
function mountSheet() {
|
|
286
|
+
if (sheet) return false;
|
|
287
|
+
returnFocus = document.activeElement;
|
|
288
|
+
state = { note: null, images: [], list: [] };
|
|
289
|
+
sheet = document.createElement('div');
|
|
290
|
+
sheet.className = 'pn';
|
|
291
|
+
sheet.id = 'page-notes';
|
|
292
|
+
sheet.innerHTML = shellHtml(page());
|
|
293
|
+
sheet.addEventListener('click', onClick);
|
|
294
|
+
document.addEventListener('keydown', onKey, true);
|
|
295
|
+
document.body.appendChild(sheet);
|
|
296
|
+
document.documentElement.classList.add('pn-open');
|
|
297
|
+
var c = sheet.querySelector('[data-pn-close]');
|
|
298
|
+
if (c) c.focus();
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
function close() {
|
|
302
|
+
if (!sheet) return;
|
|
303
|
+
save();
|
|
304
|
+
if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
|
|
305
|
+
document.removeEventListener('keydown', onKey, true);
|
|
306
|
+
if (sheet.parentNode) sheet.parentNode.removeChild(sheet);
|
|
307
|
+
sheet = null;
|
|
308
|
+
document.documentElement.classList.remove('pn-open');
|
|
309
|
+
if (returnFocus && returnFocus.focus) { try { returnFocus.focus(); } catch (_) { /* gone */ } }
|
|
310
|
+
returnFocus = null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
window.OTBPageNotes = {
|
|
314
|
+
open: function () { if (mountSheet()) openDraft(); },
|
|
315
|
+
openNote: function (id) { if (mountSheet()) refreshList().then(function () { return showNote(id); }); },
|
|
316
|
+
close: close,
|
|
317
|
+
renderBody: renderBody,
|
|
318
|
+
page: page,
|
|
319
|
+
};
|
|
320
|
+
})();
|
|
@@ -115,9 +115,9 @@
|
|
|
115
115
|
|
|
116
116
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
117
117
|
|
|
118
|
-
<script src="/builders/shell-trail.js?v=2026-10-
|
|
119
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
120
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
118
|
+
<script src="/builders/shell-trail.js?v=2026-10-04-palette"></script>
|
|
119
|
+
<script src="/builders/craft-mode.js?v=2026-10-04-palette"></script>
|
|
120
|
+
<script src="/builders/shell.js?v=2026-10-04-palette"></script>
|
|
121
121
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
122
122
|
<script src="/builders/palette.js" defer></script>
|
|
123
123
|
<!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
|
|
@@ -206,9 +206,9 @@
|
|
|
206
206
|
|
|
207
207
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
208
208
|
chrome exists before the page script runs. -->
|
|
209
|
-
<script src="/builders/shell-trail.js?v=2026-10-
|
|
210
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
211
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
209
|
+
<script src="/builders/shell-trail.js?v=2026-10-04-palette"></script>
|
|
210
|
+
<script src="/builders/craft-mode.js?v=2026-10-04-palette"></script>
|
|
211
|
+
<script src="/builders/shell.js?v=2026-10-04-palette"></script>
|
|
212
212
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
213
213
|
<script src="/builders/palette.js" defer></script>
|
|
214
214
|
<!-- shared frontend toolkit (window.OTB) — loaded before profile.js, which pulls from it. -->
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
|
|
59
59
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
60
60
|
|
|
61
|
-
<script src="/builders/shell-trail.js?v=2026-10-
|
|
62
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
63
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
61
|
+
<script src="/builders/shell-trail.js?v=2026-10-04-palette"></script>
|
|
62
|
+
<script src="/builders/craft-mode.js?v=2026-10-04-palette"></script>
|
|
63
|
+
<script src="/builders/shell.js?v=2026-10-04-palette"></script>
|
|
64
64
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
65
65
|
<script src="/builders/palette.js" defer></script>
|
|
66
66
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -77,9 +77,9 @@
|
|
|
77
77
|
|
|
78
78
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
79
79
|
chrome exists before the page script runs. -->
|
|
80
|
-
<script src="/builders/shell-trail.js?v=2026-10-
|
|
81
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
82
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
80
|
+
<script src="/builders/shell-trail.js?v=2026-10-04-palette"></script>
|
|
81
|
+
<script src="/builders/craft-mode.js?v=2026-10-04-palette"></script>
|
|
82
|
+
<script src="/builders/shell.js?v=2026-10-04-palette"></script>
|
|
83
83
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
84
84
|
<script src="/builders/palette.js" defer></script>
|
|
85
85
|
<!-- shared frontend toolkit (window.OTB) — loaded before ranks.js, which pulls from it. -->
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
|
|
42
42
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
43
43
|
|
|
44
|
-
<script src="/builders/shell-trail.js?v=2026-10-
|
|
45
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
46
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
44
|
+
<script src="/builders/shell-trail.js?v=2026-10-04-palette"></script>
|
|
45
|
+
<script src="/builders/craft-mode.js?v=2026-10-04-palette"></script>
|
|
46
|
+
<script src="/builders/shell.js?v=2026-10-04-palette"></script>
|
|
47
47
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
48
48
|
<script src="/builders/palette.js" defer></script>
|
|
49
49
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
|
|
54
54
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
55
55
|
chrome exists before the page script runs. -->
|
|
56
|
-
<script src="/builders/shell-trail.js?v=2026-10-
|
|
57
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
58
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
56
|
+
<script src="/builders/shell-trail.js?v=2026-10-04-palette"></script>
|
|
57
|
+
<script src="/builders/craft-mode.js?v=2026-10-04-palette"></script>
|
|
58
|
+
<script src="/builders/shell.js?v=2026-10-04-palette"></script>
|
|
59
59
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
60
60
|
<script src="/builders/palette.js" defer></script>
|
|
61
61
|
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
@@ -111,9 +111,9 @@
|
|
|
111
111
|
|
|
112
112
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
113
113
|
chrome exists before the page script runs. -->
|
|
114
|
-
<script src="/builders/shell-trail.js?v=2026-10-
|
|
115
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
116
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
114
|
+
<script src="/builders/shell-trail.js?v=2026-10-04-palette"></script>
|
|
115
|
+
<script src="/builders/craft-mode.js?v=2026-10-04-palette"></script>
|
|
116
|
+
<script src="/builders/shell.js?v=2026-10-04-palette"></script>
|
|
117
117
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
118
118
|
<script src="/builders/palette.js" defer></script>
|
|
119
119
|
<!-- shared frontend toolkit (window.OTB) — loaded BEFORE sessions.js so it can pull from it. -->
|
|
@@ -435,9 +435,9 @@
|
|
|
435
435
|
|
|
436
436
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
437
437
|
chrome exists before the page scripts run. -->
|
|
438
|
-
<script src="/builders/shell-trail.js?v=2026-10-
|
|
439
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
440
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
438
|
+
<script src="/builders/shell-trail.js?v=2026-10-04-palette"></script>
|
|
439
|
+
<script src="/builders/craft-mode.js?v=2026-10-04-palette"></script>
|
|
440
|
+
<script src="/builders/shell.js?v=2026-10-04-palette"></script>
|
|
441
441
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
442
442
|
<script src="/builders/palette.js" defer></script>
|
|
443
443
|
<!-- shared frontend toolkit (window.OTB) — loaded before consuming IIFEs. -->
|
|
@@ -22,6 +22,19 @@ body[data-page="street"] {
|
|
|
22
22
|
|
|
23
23
|
.street-card__body { display: flex; flex-direction: column; gap: 11px; }
|
|
24
24
|
|
|
25
|
+
/* sized to its words (task 1004568): no min-height anywhere, and every part
|
|
26
|
+
of the compact card is held to its lines. The model cuts at a word boundary
|
|
27
|
+
(street-card.js LINE); these are the net under that, so a wide run of glyphs
|
|
28
|
+
can never push a part onto another line and stretch the card. The kicker
|
|
29
|
+
gives way to "when" and the close. The title is cut to about two lines;
|
|
30
|
+
its clamp at three is only the net, for a run of wide glyphs. */
|
|
31
|
+
.street-card__head .street-label { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
32
|
+
.street-card__head #streetCardWhen { flex: none; }
|
|
33
|
+
.street-card__title { overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; flex: none; }
|
|
34
|
+
.mcard-fact { margin: 0; font-size: 14px; line-height: 1.4; color: var(--ink-soft); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
35
|
+
.street-card__lines li { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
36
|
+
.mcard-voice__words { overflow-wrap: anywhere; }
|
|
37
|
+
|
|
25
38
|
.mcard-quote { margin: 0; font-size: 14px; line-height: 1.45; color: var(--ink-soft); }
|
|
26
39
|
|
|
27
40
|
/* the wording change: struck, then inserted */
|
|
@@ -101,10 +101,25 @@
|
|
|
101
101
|
if (names.length <= 1) return names.join('');
|
|
102
102
|
return names.slice(0, -1).join(', ') + ' and ' + names[names.length - 1];
|
|
103
103
|
}
|
|
104
|
+
// a text as one run of words: every whitespace run is one space
|
|
105
|
+
function norm(s) { return str(s).replace(/\s+/g, ' ').trim(); }
|
|
104
106
|
function clip(s, n) {
|
|
105
|
-
var t =
|
|
107
|
+
var t = norm(s);
|
|
106
108
|
return t.length > n ? t.slice(0, n - 1).replace(/\s+\S*$/, '') + '…' : t;
|
|
107
109
|
}
|
|
110
|
+
// THE COMPACT CARD'S LINE RULE (task 1004568). The card is only as tall as
|
|
111
|
+
// its words need: every part of it has a cap, and anything longer is cut at
|
|
112
|
+
// a word boundary with an ellipsis (clip(): the desk's line rule, GovDesk.fit(),
|
|
113
|
+
// but never mid-word), with the whole text under "more". The caps are
|
|
114
|
+
// characters at the card's 390px width: the kicker, the fact and a listed
|
|
115
|
+
// line are one line; the title two; a voice's reason two; the proposer's
|
|
116
|
+
// words three.
|
|
117
|
+
var LINE = { where: 40, title: 64, fact: 44, line: 44, reason: 96, quote: 132 };
|
|
118
|
+
// the first line of a text, as words (a fact is one line of it)
|
|
119
|
+
function firstLine(s) {
|
|
120
|
+
var lines = str(s).split(/\r?\n/).map(function (l) { return l.replace(/^[#>*\-\s]+/, '').replace(/\s+/g, ' ').trim(); }).filter(Boolean);
|
|
121
|
+
return lines[0] || '';
|
|
122
|
+
}
|
|
108
123
|
// the first paragraph of a markdown text, as words
|
|
109
124
|
function firstPara(md) {
|
|
110
125
|
var paras = str(md).split(/\r?\n\s*\r?\n/).map(function (p) { return p.replace(/^[#>*\-\s]+/gm, '').replace(/\s+/g, ' ').trim(); }).filter(Boolean);
|
|
@@ -273,7 +288,7 @@
|
|
|
273
288
|
var votes = (item && item.votes) || [];
|
|
274
289
|
var out = [];
|
|
275
290
|
votes.filter(function (v) { return v.direction === 'no'; }).forEach(function (v) {
|
|
276
|
-
out.push({ side: 'against', by: firstName(personName(v)) + ' · objects to ' + sectionWords(v.section_key), words:
|
|
291
|
+
out.push({ side: 'against', by: firstName(personName(v)) + ' · objects to ' + sectionWords(v.section_key), words: norm(v.reason_md) });
|
|
277
292
|
});
|
|
278
293
|
var yes = votes.filter(function (v) { return v.direction === 'yes'; }).map(function (v) { return firstName(personName(v)); });
|
|
279
294
|
if (yes.length && out.length) out.unshift({ side: 'for', by: 'for', words: list(yes) });
|
|
@@ -319,7 +334,7 @@
|
|
|
319
334
|
var kind = subjectKind(item);
|
|
320
335
|
var proposer = m.proposer ? firstName(m.proposer.name || m.proposer.login) : '';
|
|
321
336
|
var words = proposerWords(item);
|
|
322
|
-
if (words) cv.quote = { by: proposer, words:
|
|
337
|
+
if (words) cv.quote = { by: proposer, words: words };
|
|
323
338
|
cv.change = changeOf(item, ctx.docket, ctx.constitution);
|
|
324
339
|
cv.revision = revisionOf(item, ctx.room);
|
|
325
340
|
var b = ballotOf(item, m, ctx.viewerId);
|
|
@@ -399,6 +414,9 @@
|
|
|
399
414
|
var who = m.proposer ? str(m.proposer.name || m.proposer.login) : '';
|
|
400
415
|
cv.subject = { kind: m.source, id: id, who: who, goalId: m.source === 'goal_join_requests' ? joinGoalId(m) : null };
|
|
401
416
|
var mine = m.state === 'needs';
|
|
417
|
+
// its one fact (task 1004568): the first line of the matter's detail (the
|
|
418
|
+
// switch's reason, a blocker's body), cut by compact(); "more" has it whole
|
|
419
|
+
if (m.detail) cv.fact = firstLine(m.detail);
|
|
402
420
|
if (m.source === 'access_requests') {
|
|
403
421
|
if (who) cv.asked = who + ' asked to join as a builder' + (/vouched/.test(str(m.where)) ? ', and someone vouched for them.' : '.');
|
|
404
422
|
if (!mine) { cv.readOnly = true; cv.result = 'Not yours to decide.'; return cv; }
|
|
@@ -470,7 +488,7 @@
|
|
|
470
488
|
if (cv.revision) history.push('an earlier sitting sent it back to be revised');
|
|
471
489
|
(item.votes || []).slice().sort(function (a, b) { return (Date.parse(a.cast_at) || 0) - (Date.parse(b.cast_at) || 0); }).forEach(function (v) {
|
|
472
490
|
var by = personName(v) || 'someone';
|
|
473
|
-
var said = v.direction === 'yes' ? by + ' voted for' : by + ' objected to ' + sectionWords(v.section_key) + (v.reason_md ? ': ' +
|
|
491
|
+
var said = v.direction === 'yes' ? by + ' voted for' : by + ' objected to ' + sectionWords(v.section_key) + (v.reason_md ? ': ' + bound(str(v.reason_md).replace(/\s+/g, ' ').trim()) : '');
|
|
474
492
|
history.push([dayOf(v.cast_at), said].filter(Boolean).join(' · '));
|
|
475
493
|
});
|
|
476
494
|
} else if (m) {
|
|
@@ -497,9 +515,9 @@
|
|
|
497
515
|
key: short.key, building: short.building, where: short.where, when: short.when, title: short.title, href: short.href,
|
|
498
516
|
source: null, quote: null, change: null, revision: null, seats: [], tally: '', voices: [], counsel: null,
|
|
499
517
|
result: short.result, actions: [], textActions: [], reviseForm: null, readOnly: false, loading: false, lines: short.lines || [], subject: null,
|
|
500
|
-
more: null, row: null,
|
|
518
|
+
more: null, row: null, fact: '',
|
|
501
519
|
};
|
|
502
|
-
if (short.key === 'queue') { cv.source = 'queue'; cv.readOnly = true; return cv; }
|
|
520
|
+
if (short.key === 'queue') { cv.source = 'queue'; cv.readOnly = true; return compact(cv); }
|
|
503
521
|
var m = findMatter(c.docket, c.key);
|
|
504
522
|
cv.source = m.source;
|
|
505
523
|
var viewerId = c.viewerId != null ? c.viewerId : (c.docket && c.docket.viewer ? c.docket.viewer.id : null);
|
|
@@ -511,14 +529,47 @@
|
|
|
511
529
|
cv.readOnly = true;
|
|
512
530
|
cv.tally = short.tally;
|
|
513
531
|
cv.result = c.room ? 'This sitting could not be read just now.' : 'Reading the sitting…';
|
|
514
|
-
return cv;
|
|
532
|
+
return compact(cv);
|
|
515
533
|
}
|
|
516
534
|
boardCard(cv, m, item, { docket: c.docket, room: c.room, constitution: c.constitution, viewerId: viewerId });
|
|
517
535
|
cv.more = moreOf(cv, m, item);
|
|
518
|
-
return cv;
|
|
536
|
+
return compact(cv);
|
|
519
537
|
}
|
|
520
538
|
sourceCard(cv, m);
|
|
521
539
|
cv.more = moreOf(cv, m, null);
|
|
540
|
+
return compact(cv);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Every part of the compact card under the line rule (task 1004568): the
|
|
544
|
+
// kicker, the title, the fact, the proposer's words, the listed lines and a
|
|
545
|
+
// voice's reason. What
|
|
546
|
+
// was cut and is not already in "more" is added there whole, so nothing a
|
|
547
|
+
// cut hides is lost.
|
|
548
|
+
function compact(cv) {
|
|
549
|
+
var whole = [];
|
|
550
|
+
function cut(s, n) {
|
|
551
|
+
var t = norm(s);
|
|
552
|
+
var c = clip(t, n);
|
|
553
|
+
if (c !== t) whole.push(t);
|
|
554
|
+
return c;
|
|
555
|
+
}
|
|
556
|
+
cv.where = cut(cv.where, LINE.where);
|
|
557
|
+
cv.title = cut(cv.title, LINE.title);
|
|
558
|
+
cv.fact = cut(cv.fact, LINE.fact);
|
|
559
|
+
if (cv.quote && cv.quote.words) cv.quote = { by: cv.quote.by, words: cut(cv.quote.words, LINE.quote) };
|
|
560
|
+
cv.lines = (cv.lines || []).map(function (l) { return cut(l, LINE.line); });
|
|
561
|
+
cv.voices = (cv.voices || []).map(function (v) {
|
|
562
|
+
var w = cut(v.words, LINE.reason);
|
|
563
|
+
return w === v.words ? v : { side: v.side, by: v.by, words: w };
|
|
564
|
+
});
|
|
565
|
+
if (whole.length) {
|
|
566
|
+
var mo = cv.more || { text: [], who: '', when: '', history: [] };
|
|
567
|
+
var have = mo.text.map(function (x) { return x.words; }).concat(mo.history).join('\n');
|
|
568
|
+
// compared as "more" holds it: bound() may have shortened a long text there
|
|
569
|
+
var add = whole.filter(function (t) { return have.indexOf(bound(t).replace(/…$/, '')) < 0; }).map(function (t) { return { label: '', words: bound(t) }; });
|
|
570
|
+
mo.text = add.concat(mo.text);
|
|
571
|
+
cv.more = mo;
|
|
572
|
+
}
|
|
522
573
|
return cv;
|
|
523
574
|
}
|
|
524
575
|
|
|
@@ -807,6 +858,7 @@
|
|
|
807
858
|
function render(doc, host, cv, on, ui) {
|
|
808
859
|
var u = ui || {};
|
|
809
860
|
while (host.firstChild) host.removeChild(host.firstChild);
|
|
861
|
+
if (cv.fact) host.appendChild(el(doc, 'p', 'mcard-fact', cv.fact));
|
|
810
862
|
if (cv.quote && cv.quote.words) host.appendChild(el(doc, 'p', 'mcard-quote', (cv.quote.by ? cv.quote.by + ': ' : '') + cv.quote.words));
|
|
811
863
|
if (cv.lines && cv.lines.length) {
|
|
812
864
|
var ul = el(doc, 'ul', 'street-card__lines');
|
|
@@ -997,7 +1049,7 @@
|
|
|
997
1049
|
var api = {
|
|
998
1050
|
API: API, SECTIONS: SECTIONS, needsRoom: needsRoom, itemIdOf: itemIdOf, subjectKind: subjectKind,
|
|
999
1051
|
model: model, actsCard: actsCard, moreOf: moreOf, request: request, orderRequest: orderRequest, check: check, answer: answer, refusal: refusal, render: render,
|
|
1000
|
-
memberWords: memberWords, windowWords: windowWords,
|
|
1052
|
+
memberWords: memberWords, windowWords: windowWords, LINE: LINE, compact: compact,
|
|
1001
1053
|
// the wording change, for a drafted amendment's struck and inserted text (task 1004530)
|
|
1002
1054
|
change: changeOf,
|
|
1003
1055
|
};
|