@unson/brainbase-mcp 0.20.0 → 0.21.1

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.
@@ -133,3 +133,63 @@ export function changesSince(lastVisit, businesses, judgmentsByBusiness, rows) {
133
133
  const plaza = (rows ?? []).filter((row) => newer(row.latest_recorded_at)).map((row) => row.key);
134
134
  return { since: new Date(since).toISOString(), cities, plaza };
135
135
  }
136
+
137
+ /**
138
+ * The lots inside a district (story-world-work-sites-and-gaps-v1, AC-12, AC-16, AC-17): a main street along
139
+ * z, a back street on each side, and lots facing them in four columns. Work with the same `purpose_label`
140
+ * stands in one block (a street of its own); blocks run from the hall toward the gate in the order of their
141
+ * oldest task, and work with no label stands last, by the gate. Inside a block the lots fill in order of
142
+ * creation (`created_at`, then id), row by row: left front, right front, left back, right back. So a new
143
+ * task never moves an older one and a change of state never moves any; only when a block's rows are full do
144
+ * the blocks after it shift one row toward the gate. A cross street separates the blocks.
145
+ * Returns `{ lots: { [taskId]: { x, z, facing, street } }, streets: [{ key, label, count, z_from, z_to }],
146
+ * rows, length }`, `facing` being +1 when the lot's front looks toward +x, `label` null for the unlabelled.
147
+ */
148
+ export const DISTRICT_STREETS = Object.freeze({ main: 2, frontLot: 4, backStreet: 7, backLot: 10, lot: 2.8, row: 3.4, cross: 2.6 });
149
+
150
+ const LOT_COLUMNS = Object.freeze([
151
+ { x: -DISTRICT_STREETS.frontLot, facing: 1 },
152
+ { x: DISTRICT_STREETS.frontLot, facing: -1 },
153
+ { x: -DISTRICT_STREETS.backLot, facing: 1 },
154
+ { x: DISTRICT_STREETS.backLot, facing: -1 },
155
+ ]);
156
+
157
+ function byCreation(a, b) {
158
+ const at = String(a.created_at ?? '').localeCompare(String(b.created_at ?? ''));
159
+ return at !== 0 ? at : String(a.task_id).localeCompare(String(b.task_id));
160
+ }
161
+
162
+ export function districtStreetLots(sites, { minRows = 4 } = {}) {
163
+ const groups = new Map();
164
+ for (const site of sites ?? []) {
165
+ const label = typeof site.purpose_label === 'string' && site.purpose_label.trim() ? site.purpose_label.trim() : null;
166
+ const key = label ?? '';
167
+ if (!groups.has(key)) groups.set(key, { key, label, sites: [] });
168
+ groups.get(key).sites.push(site);
169
+ }
170
+ const blocks = [...groups.values()].map((group) => ({ ...group, sites: group.sites.sort(byCreation) }));
171
+ blocks.sort((a, b) => {
172
+ if ((a.label === null) !== (b.label === null)) return a.label === null ? 1 : -1;
173
+ const first = byCreation(a.sites[0], b.sites[0]);
174
+ return first !== 0 ? first : a.key.localeCompare(b.key);
175
+ });
176
+ const rowsOf = (block) => Math.max(1, Math.ceil(block.sites.length / LOT_COLUMNS.length));
177
+ const rows = blocks.reduce((sum, block) => sum + rowsOf(block), 0);
178
+ const used = rows * DISTRICT_STREETS.row + Math.max(0, blocks.length - 1) * DISTRICT_STREETS.cross;
179
+ const length = Math.max(used, minRows * DISTRICT_STREETS.row);
180
+ const round = (value) => Math.round(value * 100) / 100;
181
+ const lots = {};
182
+ const streets = [];
183
+ let cursor = -used / 2;
184
+ for (const block of blocks) {
185
+ const blockRows = rowsOf(block);
186
+ block.sites.forEach((site, index) => {
187
+ const row = Math.floor(index / LOT_COLUMNS.length);
188
+ const column = LOT_COLUMNS[index % LOT_COLUMNS.length];
189
+ lots[site.task_id] = { x: column.x, z: round(cursor + (row + 0.5) * DISTRICT_STREETS.row), facing: column.facing, street: block.key };
190
+ });
191
+ streets.push({ key: block.key, label: block.label, count: block.sites.length, z_from: round(cursor), z_to: round(cursor + blockRows * DISTRICT_STREETS.row) });
192
+ cursor += blockRows * DISTRICT_STREETS.row + DISTRICT_STREETS.cross;
193
+ }
194
+ return { lots, streets, rows, length };
195
+ }
@@ -149,6 +149,20 @@
149
149
  border-color: var(--bb-color-rail-border);
150
150
  }
151
151
 
152
+ .bb-world-label.is-street {
153
+ font-weight: 700;
154
+ background: #2f6e4f;
155
+ border-color: #255a40;
156
+ color: #ffffff;
157
+ }
158
+
159
+ .bb-world-label.is-street.is-unlabelled {
160
+ background: #f1ece1;
161
+ border-style: dashed;
162
+ border-color: #8a8270;
163
+ color: #5c5546;
164
+ }
165
+
152
166
  .bb-world-label.is-engagement {
153
167
  font-size: 11px;
154
168
  color: var(--bb-color-muted);
@@ -232,3 +246,226 @@
232
246
  text-decoration: underline;
233
247
  text-underline-offset: 2px;
234
248
  }
249
+
250
+ /* --- the work inside a city (story-world-work-sites-and-gaps-v1) --------- */
251
+ .bb-world-legend.is-work {
252
+ bottom: calc(var(--bb-space-3) + 34px);
253
+ }
254
+
255
+ .bb-world-legend[hidden] {
256
+ display: none;
257
+ }
258
+
259
+ .bb-world-chip.is-work-waiting::before { background: #7a6a9e; }
260
+ .bb-world-chip.is-work-in_progress::before { background: #2f7d86; }
261
+ .bb-world-chip.is-work-pending::before { background: #8d979e; }
262
+ .bb-world-chip.is-sign::before { background: #e3a21a; }
263
+ .bb-world-chip.is-ring::before { background: transparent; border: 2px dashed #5b4a8a; border-radius: 50%; width: 8px; height: 8px; }
264
+ .bb-world-chip.is-road::before { background: #1261ad; height: 3px; width: 14px; border-radius: 0; }
265
+
266
+ .bb-world-badge {
267
+ display: inline-block;
268
+ margin-left: 6px;
269
+ padding: 0 6px;
270
+ border-radius: 999px;
271
+ font-size: 11px;
272
+ font-weight: 700;
273
+ line-height: 1.5;
274
+ }
275
+
276
+ .bb-world-badge.is-check {
277
+ background: #fbe7bd;
278
+ color: #7a5200;
279
+ border: 1px solid #e3a21a;
280
+ }
281
+
282
+ .bb-world-badge.is-unreadable {
283
+ background: #eceeef;
284
+ color: #4b524e;
285
+ border: 1px dashed #8a9096;
286
+ }
287
+
288
+ .bb-world-label.is-site {
289
+ font-size: 10px;
290
+ padding: 1px 5px;
291
+ color: var(--bb-color-muted);
292
+ }
293
+
294
+ .bb-world-label.is-site.is-check {
295
+ border-color: #e3a21a;
296
+ }
297
+
298
+ .bb-world-label.is-link {
299
+ font-size: 11px;
300
+ background: rgb(255 255 255 / 92%);
301
+ }
302
+
303
+ .bb-world-label.is-link.is-recorded {
304
+ border-color: #1261ad;
305
+ color: #0d4a85;
306
+ }
307
+
308
+ .bb-world-label.is-link.is-inferred {
309
+ border-style: dashed;
310
+ border-color: #7a6a9e;
311
+ color: #4d3f75;
312
+ }
313
+
314
+ .bb-world-rail-list.is-plain,
315
+ .bb-world-rail-list.is-links {
316
+ list-style: none;
317
+ padding-left: 0;
318
+ }
319
+
320
+ .bb-world-link {
321
+ display: grid;
322
+ grid-template-columns: 1.4em 1fr;
323
+ column-gap: 4px;
324
+ }
325
+
326
+ .bb-world-link .bb-world-rail-note {
327
+ grid-column: 2;
328
+ }
329
+
330
+ .bb-world-link-mark {
331
+ font-weight: 700;
332
+ color: #1261ad;
333
+ }
334
+
335
+ .bb-world-link.is-inferred .bb-world-link-mark { color: #7a6a9e; }
336
+ .bb-world-link.is-unreadable .bb-world-link-mark { color: #8a9096; }
337
+ .bb-world-link.is-unreadable > span:not(.bb-world-link-mark) { color: var(--bb-color-muted); }
338
+
339
+ .bb-world-rail-pick {
340
+ font: inherit;
341
+ font-size: 13px;
342
+ text-align: left;
343
+ padding: 2px 0;
344
+ border: 0;
345
+ background: none;
346
+ color: var(--bb-color-accent);
347
+ text-decoration: underline;
348
+ text-underline-offset: 2px;
349
+ cursor: pointer;
350
+ }
351
+
352
+ .bb-world-rail-pick:disabled {
353
+ color: var(--bb-color-ink);
354
+ text-decoration: none;
355
+ cursor: default;
356
+ }
357
+
358
+ .bb-world-rail-pick.is-gap {
359
+ color: #7a5200;
360
+ }
361
+
362
+ .bb-world-swatch {
363
+ display: inline-block;
364
+ width: 10px;
365
+ height: 10px;
366
+ margin-right: 6px;
367
+ border-radius: 3px;
368
+ background: var(--bb-color-quiet);
369
+ }
370
+
371
+ .bb-world-swatch.is-waiting { background: #7a6a9e; }
372
+ .bb-world-swatch.is-in_progress { background: #2f7d86; }
373
+ .bb-world-swatch.is-pending { background: #8d979e; }
374
+ .bb-world-swatch.is-completed { background: #6f9a68; }
375
+ .bb-world-swatch.is-cancelled { background: #b9b2a5; }
376
+
377
+ .bb-world-rail-sub {
378
+ margin: var(--bb-space-2) 0 4px;
379
+ font-size: 12px;
380
+ color: var(--bb-color-muted);
381
+ }
382
+
383
+ .bb-world-rail-caveat.is-quiet {
384
+ color: var(--bb-color-muted);
385
+ }
386
+
387
+ .bb-world-gap {
388
+ padding: 8px 10px;
389
+ margin-bottom: 8px;
390
+ border-left: 3px solid #e3a21a;
391
+ background: rgb(251 231 189 / 35%);
392
+ border-radius: 4px;
393
+ }
394
+
395
+ .bb-world-gap h4 {
396
+ margin: 0 0 4px;
397
+ font-size: 13px;
398
+ }
399
+
400
+ .bb-world-original p {
401
+ white-space: pre-wrap;
402
+ font-size: 12px;
403
+ color: var(--bb-color-muted);
404
+ }
405
+
406
+ .bb-world-hud[hidden] {
407
+ display: none;
408
+ }
409
+
410
+ /* --- inside a district (AC-11〜15) ---------------------------------------- */
411
+ .bb-world-canvas[hidden],
412
+ .bb-world-labels[hidden] {
413
+ display: none;
414
+ }
415
+
416
+ .bb-world-district-legend {
417
+ position: absolute;
418
+ left: var(--bb-space-3);
419
+ bottom: var(--bb-space-3);
420
+ z-index: 3;
421
+ width: min(600px, calc(100% - 2 * var(--bb-space-3)));
422
+ box-sizing: border-box;
423
+ padding: 6px 10px;
424
+ border: 1px solid var(--bb-color-border);
425
+ border-radius: 8px;
426
+ background: rgb(255 255 255 / 92%);
427
+ font-size: 12px;
428
+ color: var(--bb-color-ink);
429
+ }
430
+
431
+ .bb-world-district-legend[hidden] {
432
+ display: none;
433
+ }
434
+
435
+ .bb-world-district-legend summary {
436
+ cursor: pointer;
437
+ font-weight: 700;
438
+ }
439
+
440
+ .bb-world-district-legend ul {
441
+ margin: 6px 0 0;
442
+ padding: 0;
443
+ list-style: none;
444
+ display: grid;
445
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
446
+ gap: 2px 14px;
447
+ }
448
+
449
+ .bb-world-district-legend li.is-ambience {
450
+ grid-column: 1 / -1;
451
+ color: var(--bb-color-muted);
452
+ margin-top: 4px;
453
+ }
454
+
455
+ .bb-world-district-notice {
456
+ position: absolute;
457
+ top: var(--bb-space-3);
458
+ left: 50%;
459
+ transform: translateX(-50%);
460
+ z-index: 3;
461
+ margin: 0;
462
+ padding: 8px 14px;
463
+ border-radius: 8px;
464
+ background: rgb(255 240 199 / 95%);
465
+ border-left: 3px solid var(--bb-color-warning);
466
+ font-size: 13px;
467
+ }
468
+
469
+ .bb-world-district-notice[hidden] {
470
+ display: none;
471
+ }