shipwright-cli 3.1.0 → 3.3.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/.claude/agents/code-reviewer.md +2 -0
- package/.claude/agents/devops-engineer.md +2 -0
- package/.claude/agents/doc-fleet-agent.md +2 -0
- package/.claude/agents/pipeline-agent.md +2 -0
- package/.claude/agents/shell-script-specialist.md +2 -0
- package/.claude/agents/test-specialist.md +2 -0
- package/.claude/hooks/agent-crash-capture.sh +32 -0
- package/.claude/hooks/post-tool-use.sh +3 -2
- package/.claude/hooks/pre-tool-use.sh +35 -3
- package/README.md +22 -8
- package/claude-code/hooks/config-change.sh +18 -0
- package/claude-code/hooks/instructions-reloaded.sh +7 -0
- package/claude-code/hooks/worktree-create.sh +25 -0
- package/claude-code/hooks/worktree-remove.sh +20 -0
- package/config/code-constitution.json +130 -0
- package/config/defaults.json +25 -2
- package/config/policy.json +1 -1
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +8 -6
- package/dashboard/public/styles.css +176 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +117 -25
- package/dashboard/services/config.ts +26 -0
- package/dashboard/services/db.ts +118 -0
- package/dashboard/src/canvas/pixel-agent.ts +298 -0
- package/dashboard/src/canvas/pixel-sprites.ts +440 -0
- package/dashboard/src/canvas/shipyard-effects.ts +367 -0
- package/dashboard/src/canvas/shipyard-scene.ts +616 -0
- package/dashboard/src/canvas/submarine-layout.ts +267 -0
- package/dashboard/src/components/header.ts +8 -7
- package/dashboard/src/core/api.ts +5 -0
- package/dashboard/src/core/router.ts +1 -0
- package/dashboard/src/design/submarine-theme.ts +253 -0
- package/dashboard/src/main.ts +2 -0
- package/dashboard/src/types/api.ts +12 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/metrics.ts +69 -1
- package/dashboard/src/views/shipyard.ts +39 -0
- package/dashboard/types/index.ts +166 -0
- package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
- package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
- package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
- package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
- package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
- package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
- package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
- package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
- package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
- package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
- package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
- package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
- package/docs/research/RESEARCH_INDEX.md +439 -0
- package/docs/research/RESEARCH_SOURCES.md +440 -0
- package/docs/research/RESEARCH_SUMMARY.txt +275 -0
- package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
- package/package.json +2 -2
- package/scripts/lib/adaptive-model.sh +427 -0
- package/scripts/lib/adaptive-timeout.sh +316 -0
- package/scripts/lib/audit-trail.sh +309 -0
- package/scripts/lib/auto-recovery.sh +471 -0
- package/scripts/lib/bandit-selector.sh +431 -0
- package/scripts/lib/bootstrap.sh +104 -2
- package/scripts/lib/causal-graph.sh +455 -0
- package/scripts/lib/compat.sh +126 -0
- package/scripts/lib/compound-audit.sh +337 -0
- package/scripts/lib/constitutional.sh +454 -0
- package/scripts/lib/context-budget.sh +359 -0
- package/scripts/lib/convergence.sh +594 -0
- package/scripts/lib/cost-optimizer.sh +634 -0
- package/scripts/lib/daemon-adaptive.sh +14 -2
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +25 -4
- package/scripts/lib/daemon-poll-github.sh +361 -0
- package/scripts/lib/daemon-poll-health.sh +299 -0
- package/scripts/lib/daemon-poll.sh +27 -611
- package/scripts/lib/daemon-state.sh +119 -66
- package/scripts/lib/daemon-triage.sh +10 -0
- package/scripts/lib/dod-scorecard.sh +442 -0
- package/scripts/lib/error-actionability.sh +300 -0
- package/scripts/lib/formal-spec.sh +461 -0
- package/scripts/lib/helpers.sh +180 -5
- package/scripts/lib/intent-analysis.sh +409 -0
- package/scripts/lib/loop-convergence.sh +350 -0
- package/scripts/lib/loop-iteration.sh +682 -0
- package/scripts/lib/loop-progress.sh +48 -0
- package/scripts/lib/loop-restart.sh +185 -0
- package/scripts/lib/memory-effectiveness.sh +506 -0
- package/scripts/lib/mutation-executor.sh +352 -0
- package/scripts/lib/outcome-feedback.sh +521 -0
- package/scripts/lib/pipeline-cli.sh +336 -0
- package/scripts/lib/pipeline-commands.sh +1216 -0
- package/scripts/lib/pipeline-detection.sh +101 -3
- package/scripts/lib/pipeline-execution.sh +897 -0
- package/scripts/lib/pipeline-github.sh +28 -3
- package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
- package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
- package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
- package/scripts/lib/pipeline-intelligence.sh +104 -1138
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -711
- package/scripts/lib/pipeline-quality-gates.sh +563 -0
- package/scripts/lib/pipeline-stages-build.sh +730 -0
- package/scripts/lib/pipeline-stages-delivery.sh +965 -0
- package/scripts/lib/pipeline-stages-intake.sh +1133 -0
- package/scripts/lib/pipeline-stages-monitor.sh +407 -0
- package/scripts/lib/pipeline-stages-review.sh +1022 -0
- package/scripts/lib/pipeline-stages.sh +161 -2901
- package/scripts/lib/pipeline-state.sh +36 -5
- package/scripts/lib/pipeline-util.sh +487 -0
- package/scripts/lib/policy-learner.sh +438 -0
- package/scripts/lib/process-reward.sh +493 -0
- package/scripts/lib/project-detect.sh +649 -0
- package/scripts/lib/quality-profile.sh +334 -0
- package/scripts/lib/recruit-commands.sh +885 -0
- package/scripts/lib/recruit-learning.sh +739 -0
- package/scripts/lib/recruit-roles.sh +648 -0
- package/scripts/lib/reward-aggregator.sh +458 -0
- package/scripts/lib/rl-optimizer.sh +362 -0
- package/scripts/lib/root-cause.sh +427 -0
- package/scripts/lib/scope-enforcement.sh +445 -0
- package/scripts/lib/session-restart.sh +493 -0
- package/scripts/lib/skill-memory.sh +300 -0
- package/scripts/lib/skill-registry.sh +775 -0
- package/scripts/lib/spec-driven.sh +476 -0
- package/scripts/lib/test-helpers.sh +18 -7
- package/scripts/lib/test-holdout.sh +429 -0
- package/scripts/lib/test-optimizer.sh +511 -0
- package/scripts/shipwright-file-suggest.sh +45 -0
- package/scripts/skills/adversarial-quality.md +61 -0
- package/scripts/skills/api-design.md +44 -0
- package/scripts/skills/architecture-design.md +50 -0
- package/scripts/skills/brainstorming.md +43 -0
- package/scripts/skills/data-pipeline.md +44 -0
- package/scripts/skills/deploy-safety.md +64 -0
- package/scripts/skills/documentation.md +38 -0
- package/scripts/skills/frontend-design.md +45 -0
- package/scripts/skills/generated/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
- package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
- package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
- package/scripts/skills/generated/cli-version-management.md +29 -0
- package/scripts/skills/generated/collection-system-validation.md +99 -0
- package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
- package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
- package/scripts/skills/generated/test-parallelization-detection.md +65 -0
- package/scripts/skills/observability.md +79 -0
- package/scripts/skills/performance.md +48 -0
- package/scripts/skills/pr-quality.md +49 -0
- package/scripts/skills/product-thinking.md +43 -0
- package/scripts/skills/security-audit.md +49 -0
- package/scripts/skills/systematic-debugging.md +40 -0
- package/scripts/skills/testing-strategy.md +47 -0
- package/scripts/skills/two-stage-review.md +52 -0
- package/scripts/skills/validation-thoroughness.md +55 -0
- package/scripts/sw +9 -3
- package/scripts/sw-activity.sh +9 -8
- package/scripts/sw-adaptive.sh +8 -7
- package/scripts/sw-adversarial.sh +2 -1
- package/scripts/sw-architecture-enforcer.sh +3 -1
- package/scripts/sw-auth.sh +12 -2
- package/scripts/sw-autonomous.sh +5 -1
- package/scripts/sw-changelog.sh +4 -1
- package/scripts/sw-checkpoint.sh +2 -1
- package/scripts/sw-ci.sh +15 -6
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +45 -20
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +107 -5
- package/scripts/sw-daemon.sh +71 -11
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +71 -20
- package/scripts/sw-decide.sh +8 -2
- package/scripts/sw-decompose.sh +360 -17
- package/scripts/sw-deps.sh +4 -1
- package/scripts/sw-developer-simulation.sh +4 -1
- package/scripts/sw-discovery.sh +378 -5
- package/scripts/sw-doc-fleet.sh +4 -1
- package/scripts/sw-docs-agent.sh +3 -1
- package/scripts/sw-docs.sh +2 -1
- package/scripts/sw-doctor.sh +453 -2
- package/scripts/sw-dora.sh +4 -1
- package/scripts/sw-durable.sh +12 -7
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +13 -4
- package/scripts/sw-evidence.sh +364 -12
- package/scripts/sw-feedback.sh +550 -9
- package/scripts/sw-fix.sh +20 -1
- package/scripts/sw-fleet-discover.sh +6 -2
- package/scripts/sw-fleet-viz.sh +9 -4
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +18 -4
- package/scripts/sw-github-checks.sh +3 -2
- package/scripts/sw-github-deploy.sh +3 -2
- package/scripts/sw-github-graphql.sh +18 -7
- package/scripts/sw-guild.sh +5 -1
- package/scripts/sw-heartbeat.sh +5 -30
- package/scripts/sw-hello.sh +67 -0
- package/scripts/sw-hygiene.sh +10 -3
- package/scripts/sw-incident.sh +273 -5
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +44 -7
- package/scripts/sw-jira.sh +5 -1
- package/scripts/sw-launchd.sh +2 -1
- package/scripts/sw-linear.sh +4 -1
- package/scripts/sw-logs.sh +4 -1
- package/scripts/sw-loop.sh +436 -1076
- package/scripts/sw-memory.sh +357 -3
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +483 -27
- package/scripts/sw-otel.sh +15 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +7 -1
- package/scripts/sw-pipeline-vitals.sh +12 -6
- package/scripts/sw-pipeline.sh +54 -2653
- package/scripts/sw-pm.sh +16 -8
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +17 -5
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +17 -4
- package/scripts/sw-quality.sh +14 -6
- package/scripts/sw-reaper.sh +8 -25
- package/scripts/sw-recruit.sh +156 -2303
- package/scripts/sw-regression.sh +19 -12
- package/scripts/sw-release-manager.sh +3 -1
- package/scripts/sw-release.sh +4 -1
- package/scripts/sw-remote.sh +3 -1
- package/scripts/sw-replay.sh +7 -1
- package/scripts/sw-retro.sh +158 -1
- package/scripts/sw-review-rerun.sh +3 -1
- package/scripts/sw-scale.sh +14 -5
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +173 -6
- package/scripts/sw-session.sh +9 -3
- package/scripts/sw-setup.sh +3 -1
- package/scripts/sw-stall-detector.sh +406 -0
- package/scripts/sw-standup.sh +15 -7
- package/scripts/sw-status.sh +3 -1
- package/scripts/sw-strategic.sh +14 -6
- package/scripts/sw-stream.sh +13 -4
- package/scripts/sw-swarm.sh +20 -7
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +7 -31
- package/scripts/sw-testgen.sh +17 -6
- package/scripts/sw-tmux-pipeline.sh +4 -1
- package/scripts/sw-tmux-role-color.sh +2 -0
- package/scripts/sw-tmux-status.sh +1 -1
- package/scripts/sw-tmux.sh +37 -1
- package/scripts/sw-trace.sh +3 -1
- package/scripts/sw-tracker-github.sh +3 -0
- package/scripts/sw-tracker-jira.sh +3 -0
- package/scripts/sw-tracker-linear.sh +3 -0
- package/scripts/sw-tracker.sh +3 -1
- package/scripts/sw-triage.sh +3 -2
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +5 -2
- package/scripts/sw-widgets.sh +9 -4
- package/scripts/sw-worktree.sh +15 -3
- package/scripts/test-skill-injection.sh +1233 -0
- package/templates/pipelines/autonomous.json +27 -3
- package/templates/pipelines/cost-aware.json +34 -8
- package/templates/pipelines/deployed.json +12 -0
- package/templates/pipelines/enterprise.json +12 -0
- package/templates/pipelines/fast.json +6 -0
- package/templates/pipelines/full.json +27 -3
- package/templates/pipelines/hotfix.json +6 -0
- package/templates/pipelines/standard.json +12 -0
- package/templates/pipelines/tdd.json +12 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// Submarine Layout System — Compartment positioning, hull geometry, and navigation paths
|
|
2
|
+
|
|
3
|
+
import type { StageName } from "../design/tokens";
|
|
4
|
+
import { COMPARTMENTS, STAGE_TO_COMPARTMENT } from "../design/submarine-theme";
|
|
5
|
+
import { layout, timing } from "../design/submarine-theme";
|
|
6
|
+
|
|
7
|
+
// ── Types ────────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface Point {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CompartmentRect {
|
|
15
|
+
stage: StageName;
|
|
16
|
+
label: string;
|
|
17
|
+
icon: string;
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
centerX: number;
|
|
23
|
+
centerY: number;
|
|
24
|
+
stationX: number; // where agent stands to work
|
|
25
|
+
stationY: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface HullGeometry {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
radius: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface PipeSegment {
|
|
37
|
+
from: Point;
|
|
38
|
+
to: Point;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── SubmarineLayout class ────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
export class SubmarineLayout {
|
|
44
|
+
compartments: CompartmentRect[] = [];
|
|
45
|
+
hull: HullGeometry = { x: 0, y: 0, width: 0, height: 0, radius: 0 };
|
|
46
|
+
pipes: PipeSegment[] = [];
|
|
47
|
+
portholes: Point[] = [];
|
|
48
|
+
width = 0;
|
|
49
|
+
height = 0;
|
|
50
|
+
|
|
51
|
+
recalculate(width: number, height: number): void {
|
|
52
|
+
this.width = width;
|
|
53
|
+
this.height = height;
|
|
54
|
+
|
|
55
|
+
// Compute available space
|
|
56
|
+
const depthGaugeWidth = layout.depthGaugeWidth;
|
|
57
|
+
const gap = 12; // gap between depth gauge and hull
|
|
58
|
+
const manifestHeight = layout.manifestHeight;
|
|
59
|
+
const hullPadding = layout.hullPadding;
|
|
60
|
+
const compartmentGap = layout.compartmentGap;
|
|
61
|
+
|
|
62
|
+
const availableWidth = width - depthGaugeWidth - gap;
|
|
63
|
+
const availableHeight = height - manifestHeight;
|
|
64
|
+
|
|
65
|
+
// Hull takes up ~85% of height, centered vertically
|
|
66
|
+
const hullHeightRatio = 0.85;
|
|
67
|
+
const hullHeight = Math.floor(availableHeight * hullHeightRatio);
|
|
68
|
+
const hullWidth = availableWidth - gap;
|
|
69
|
+
|
|
70
|
+
// Center hull vertically
|
|
71
|
+
const topMargin = Math.floor((availableHeight - hullHeight) / 2);
|
|
72
|
+
const hullX = depthGaugeWidth + gap;
|
|
73
|
+
const hullY = topMargin;
|
|
74
|
+
|
|
75
|
+
this.hull = {
|
|
76
|
+
x: hullX,
|
|
77
|
+
y: hullY,
|
|
78
|
+
width: hullWidth,
|
|
79
|
+
height: hullHeight,
|
|
80
|
+
radius: layout.hullRadius,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Compute compartment layout: 3 rows
|
|
84
|
+
this.computeCompartments();
|
|
85
|
+
this.computePipes();
|
|
86
|
+
this.computePortholes();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private computeCompartments(): void {
|
|
90
|
+
this.compartments = [];
|
|
91
|
+
|
|
92
|
+
const innerX = this.hull.x + layout.hullPadding;
|
|
93
|
+
const innerY = this.hull.y + layout.hullPadding;
|
|
94
|
+
const innerWidth = this.hull.width - layout.hullPadding * 2;
|
|
95
|
+
const innerHeight = this.hull.height - layout.hullPadding * 2;
|
|
96
|
+
|
|
97
|
+
// Row sizes: 4, 4, 3 compartments
|
|
98
|
+
const rowSizes = [4, 4, 3];
|
|
99
|
+
const numRows = rowSizes.length;
|
|
100
|
+
const rowHeight =
|
|
101
|
+
(innerHeight - (numRows - 1) * layout.compartmentGap) / numRows;
|
|
102
|
+
|
|
103
|
+
for (let rowIdx = 0; rowIdx < numRows; rowIdx++) {
|
|
104
|
+
const colCount = rowSizes[rowIdx];
|
|
105
|
+
const colWidth =
|
|
106
|
+
(innerWidth - (colCount - 1) * layout.compartmentGap) / colCount;
|
|
107
|
+
|
|
108
|
+
const rowY = innerY + rowIdx * (rowHeight + layout.compartmentGap);
|
|
109
|
+
|
|
110
|
+
for (let colIdx = 0; colIdx < colCount; colIdx++) {
|
|
111
|
+
const compartment = COMPARTMENTS.find(
|
|
112
|
+
(c) => c.row === rowIdx && c.col === colIdx,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (!compartment) continue;
|
|
116
|
+
|
|
117
|
+
const x = innerX + colIdx * (colWidth + layout.compartmentGap);
|
|
118
|
+
|
|
119
|
+
const rect: CompartmentRect = {
|
|
120
|
+
stage: compartment.stage,
|
|
121
|
+
label: compartment.label,
|
|
122
|
+
icon: compartment.icon,
|
|
123
|
+
x,
|
|
124
|
+
y: rowY,
|
|
125
|
+
width: colWidth,
|
|
126
|
+
height: rowHeight,
|
|
127
|
+
centerX: x + colWidth / 2,
|
|
128
|
+
centerY: rowY + rowHeight / 2,
|
|
129
|
+
stationX: x + colWidth / 2,
|
|
130
|
+
stationY: rowY + rowHeight / 2,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
this.compartments.push(rect);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private computePipes(): void {
|
|
139
|
+
this.pipes = [];
|
|
140
|
+
|
|
141
|
+
// Connect adjacent compartments in the same row horizontally
|
|
142
|
+
const compartmentsByRow: { [key: number]: CompartmentRect[] } = {};
|
|
143
|
+
for (const comp of this.compartments) {
|
|
144
|
+
const compartment = COMPARTMENTS.find((c) => c.stage === comp.stage);
|
|
145
|
+
if (compartment) {
|
|
146
|
+
if (!compartmentsByRow[compartment.row]) {
|
|
147
|
+
compartmentsByRow[compartment.row] = [];
|
|
148
|
+
}
|
|
149
|
+
compartmentsByRow[compartment.row].push(comp);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Connect horizontally within rows
|
|
154
|
+
for (const row of Object.values(compartmentsByRow)) {
|
|
155
|
+
row.sort((a, b) => a.x - b.x);
|
|
156
|
+
for (let i = 0; i < row.length - 1; i++) {
|
|
157
|
+
this.pipes.push({
|
|
158
|
+
from: { x: row[i].x + row[i].width, y: row[i].centerY },
|
|
159
|
+
to: { x: row[i + 1].x, y: row[i + 1].centerY },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Connect rows vertically at the end of each row
|
|
165
|
+
for (let rowIdx = 0; rowIdx < 2; rowIdx++) {
|
|
166
|
+
const currentRow = compartmentsByRow[rowIdx];
|
|
167
|
+
const nextRow = compartmentsByRow[rowIdx + 1];
|
|
168
|
+
if (currentRow && nextRow) {
|
|
169
|
+
const lastInCurrent = currentRow[currentRow.length - 1];
|
|
170
|
+
const firstInNext = nextRow[0];
|
|
171
|
+
this.pipes.push({
|
|
172
|
+
from: {
|
|
173
|
+
x: lastInCurrent.centerX,
|
|
174
|
+
y: lastInCurrent.y + lastInCurrent.height,
|
|
175
|
+
},
|
|
176
|
+
to: { x: firstInNext.centerX, y: firstInNext.y },
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private computePortholes(): void {
|
|
183
|
+
this.portholes = [];
|
|
184
|
+
|
|
185
|
+
const portholesPerEdge = 4;
|
|
186
|
+
const topY = this.hull.y + layout.portholeRadius;
|
|
187
|
+
const bottomY = this.hull.y + this.hull.height - layout.portholeRadius;
|
|
188
|
+
|
|
189
|
+
// Top portholes
|
|
190
|
+
for (let i = 0; i < portholesPerEdge; i++) {
|
|
191
|
+
const x =
|
|
192
|
+
this.hull.x +
|
|
193
|
+
layout.portholeRadius +
|
|
194
|
+
(i * (this.hull.width - 2 * layout.portholeRadius)) /
|
|
195
|
+
(portholesPerEdge - 1);
|
|
196
|
+
this.portholes.push({ x, y: topY });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Bottom portholes
|
|
200
|
+
for (let i = 0; i < portholesPerEdge; i++) {
|
|
201
|
+
const x =
|
|
202
|
+
this.hull.x +
|
|
203
|
+
layout.portholeRadius +
|
|
204
|
+
(i * (this.hull.width - 2 * layout.portholeRadius)) /
|
|
205
|
+
(portholesPerEdge - 1);
|
|
206
|
+
this.portholes.push({ x, y: bottomY });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getCompartment(stage: StageName): CompartmentRect | undefined {
|
|
211
|
+
return this.compartments.find((c) => c.stage === stage);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getPathBetween(from: StageName, to: StageName): Point[] {
|
|
215
|
+
const fromComp = this.getCompartment(from);
|
|
216
|
+
const toComp = this.getCompartment(to);
|
|
217
|
+
|
|
218
|
+
if (!fromComp || !toComp) return [];
|
|
219
|
+
if (from === to) return [{ x: fromComp.stationX, y: fromComp.stationY }];
|
|
220
|
+
|
|
221
|
+
const fromCompartmentDef = STAGE_TO_COMPARTMENT[from];
|
|
222
|
+
const toCompartmentDef = STAGE_TO_COMPARTMENT[to];
|
|
223
|
+
|
|
224
|
+
const fromRow = fromCompartmentDef.row;
|
|
225
|
+
const toRow = toCompartmentDef.row;
|
|
226
|
+
|
|
227
|
+
const waypoints: Point[] = [{ x: fromComp.stationX, y: fromComp.stationY }];
|
|
228
|
+
|
|
229
|
+
if (fromRow === toRow) {
|
|
230
|
+
// Same row: straight horizontal path
|
|
231
|
+
waypoints.push({ x: toComp.stationX, y: toComp.stationY });
|
|
232
|
+
} else {
|
|
233
|
+
// Different rows: exit compartment, move to row connector, vertical, then horizontal to target
|
|
234
|
+
|
|
235
|
+
// Move to right edge of current compartment
|
|
236
|
+
waypoints.push({
|
|
237
|
+
x: fromComp.x + fromComp.width + layout.compartmentGap / 2,
|
|
238
|
+
y: fromComp.centerY,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Move vertically to target row
|
|
242
|
+
waypoints.push({
|
|
243
|
+
x: fromComp.x + fromComp.width + layout.compartmentGap / 2,
|
|
244
|
+
y: toComp.centerY,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Move horizontally to target
|
|
248
|
+
waypoints.push({ x: toComp.stationX, y: toComp.stationY });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return waypoints;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
hitTestCompartment(x: number, y: number): CompartmentRect | undefined {
|
|
255
|
+
for (const comp of this.compartments) {
|
|
256
|
+
if (
|
|
257
|
+
x >= comp.x &&
|
|
258
|
+
x <= comp.x + comp.width &&
|
|
259
|
+
y >= comp.y &&
|
|
260
|
+
y <= comp.y + comp.height
|
|
261
|
+
) {
|
|
262
|
+
return comp;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -35,10 +35,10 @@ function setupSoundToggle(): void {
|
|
|
35
35
|
btn.innerHTML = soundEnabled ? "\u{1F50A} Sound" : "\u{1F507} Mute";
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
// Insert before user
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
41
|
-
headerActions.insertBefore(btn,
|
|
38
|
+
// Insert before user menu if possible
|
|
39
|
+
const userMenu = document.getElementById("user-menu");
|
|
40
|
+
if (userMenu && userMenu.parentNode === headerActions) {
|
|
41
|
+
headerActions.insertBefore(btn, userMenu);
|
|
42
42
|
} else {
|
|
43
43
|
headerActions.appendChild(btn);
|
|
44
44
|
}
|
|
@@ -71,11 +71,12 @@ function setupThemeToggle(): void {
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
const soundBtn = document.getElementById("sound-toggle");
|
|
74
|
-
if (soundBtn) {
|
|
74
|
+
if (soundBtn && soundBtn.parentNode === headerActions) {
|
|
75
75
|
headerActions.insertBefore(btn, soundBtn);
|
|
76
76
|
} else {
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
77
|
+
const userMenu = document.getElementById("user-menu");
|
|
78
|
+
if (userMenu && userMenu.parentNode === headerActions)
|
|
79
|
+
headerActions.insertBefore(btn, userMenu);
|
|
79
80
|
else headerActions.appendChild(btn);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
MachineInfo,
|
|
8
8
|
JoinToken,
|
|
9
9
|
CostBreakdown,
|
|
10
|
+
ContextEfficiency,
|
|
10
11
|
DaemonConfig,
|
|
11
12
|
AlertInfo,
|
|
12
13
|
InsightsData,
|
|
@@ -113,6 +114,10 @@ export const fetchCostTrend = (period = 30) =>
|
|
|
113
114
|
`/api/costs/trend?period=${period}`,
|
|
114
115
|
);
|
|
115
116
|
|
|
117
|
+
// Context efficiency
|
|
118
|
+
export const fetchContextEfficiency = (period = 7) =>
|
|
119
|
+
request<ContextEfficiency>(`/api/context-efficiency?period=${period}`);
|
|
120
|
+
|
|
116
121
|
// Daemon
|
|
117
122
|
export const fetchDaemonConfig = () =>
|
|
118
123
|
request<DaemonConfig>("/api/daemon/config");
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// Submarine Theme — Nautical design constants for the Shipyard tab
|
|
2
|
+
// Maps pipeline stages to submarine compartments, crew roles, and visual styling.
|
|
3
|
+
|
|
4
|
+
import type { StageName } from "./tokens";
|
|
5
|
+
|
|
6
|
+
// ── Compartment definitions ──────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export interface Compartment {
|
|
9
|
+
stage: StageName;
|
|
10
|
+
label: string;
|
|
11
|
+
icon: string;
|
|
12
|
+
row: number; // 0=top, 1=mid, 2=bottom
|
|
13
|
+
col: number; // position within row
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const COMPARTMENTS: Compartment[] = [
|
|
17
|
+
// Row 0 — upper deck
|
|
18
|
+
{ stage: "intake", label: "Airlock", icon: "\u{1F6AA}", row: 0, col: 0 },
|
|
19
|
+
{ stage: "plan", label: "Bridge", icon: "\u{1F9ED}", row: 0, col: 1 },
|
|
20
|
+
{ stage: "design", label: "Nav Room", icon: "\u{1F4D0}", row: 0, col: 2 },
|
|
21
|
+
{
|
|
22
|
+
stage: "build",
|
|
23
|
+
label: "Engine Room",
|
|
24
|
+
icon: "\u2699\uFE0F",
|
|
25
|
+
row: 0,
|
|
26
|
+
col: 3,
|
|
27
|
+
},
|
|
28
|
+
// Row 1 — mid deck
|
|
29
|
+
{ stage: "test", label: "Testing Bay", icon: "\u{1F52C}", row: 1, col: 0 },
|
|
30
|
+
{ stage: "review", label: "War Room", icon: "\u{1F4CB}", row: 1, col: 1 },
|
|
31
|
+
{
|
|
32
|
+
stage: "compound_quality",
|
|
33
|
+
label: "Sonar Room",
|
|
34
|
+
icon: "\u{1F4E1}",
|
|
35
|
+
row: 1,
|
|
36
|
+
col: 2,
|
|
37
|
+
},
|
|
38
|
+
{ stage: "pr", label: "Comms", icon: "\u{1F4FB}", row: 1, col: 3 },
|
|
39
|
+
// Row 2 — lower deck
|
|
40
|
+
{ stage: "merge", label: "Reactor", icon: "\u269B\uFE0F", row: 2, col: 0 },
|
|
41
|
+
{ stage: "deploy", label: "Ballast", icon: "\u{1F680}", row: 2, col: 1 },
|
|
42
|
+
{ stage: "monitor", label: "Periscope", icon: "\u{1F52D}", row: 2, col: 2 },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export const STAGE_TO_COMPARTMENT: Record<StageName, Compartment> =
|
|
46
|
+
Object.fromEntries(COMPARTMENTS.map((c) => [c.stage, c])) as Record<
|
|
47
|
+
StageName,
|
|
48
|
+
Compartment
|
|
49
|
+
>;
|
|
50
|
+
|
|
51
|
+
// ── Crew roles ───────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
export type CrewRole =
|
|
54
|
+
| "captain"
|
|
55
|
+
| "engineer"
|
|
56
|
+
| "navigator"
|
|
57
|
+
| "operator"
|
|
58
|
+
| "medic"
|
|
59
|
+
| "sonarTech";
|
|
60
|
+
|
|
61
|
+
export interface CrewPalette {
|
|
62
|
+
role: CrewRole;
|
|
63
|
+
/** Primary uniform hue (0-360) */
|
|
64
|
+
hue: number;
|
|
65
|
+
/** Saturation % */
|
|
66
|
+
sat: number;
|
|
67
|
+
/** Lightness for body */
|
|
68
|
+
bodyL: number;
|
|
69
|
+
/** Lightness for highlight/trim */
|
|
70
|
+
trimL: number;
|
|
71
|
+
/** Accent color hex (hat, badge, accessory) */
|
|
72
|
+
accent: string;
|
|
73
|
+
/** Skin tone base (HSL lightness) */
|
|
74
|
+
skinL: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const CREW_PALETTES: Record<CrewRole, CrewPalette> = {
|
|
78
|
+
captain: {
|
|
79
|
+
role: "captain",
|
|
80
|
+
hue: 220,
|
|
81
|
+
sat: 70,
|
|
82
|
+
bodyL: 25,
|
|
83
|
+
trimL: 55,
|
|
84
|
+
accent: "#ffd700",
|
|
85
|
+
skinL: 72,
|
|
86
|
+
},
|
|
87
|
+
engineer: {
|
|
88
|
+
role: "engineer",
|
|
89
|
+
hue: 25,
|
|
90
|
+
sat: 85,
|
|
91
|
+
bodyL: 45,
|
|
92
|
+
trimL: 60,
|
|
93
|
+
accent: "#ff8c00",
|
|
94
|
+
skinL: 72,
|
|
95
|
+
},
|
|
96
|
+
navigator: {
|
|
97
|
+
role: "navigator",
|
|
98
|
+
hue: 140,
|
|
99
|
+
sat: 55,
|
|
100
|
+
bodyL: 30,
|
|
101
|
+
trimL: 55,
|
|
102
|
+
accent: "#2ecc71",
|
|
103
|
+
skinL: 72,
|
|
104
|
+
},
|
|
105
|
+
operator: {
|
|
106
|
+
role: "operator",
|
|
107
|
+
hue: 50,
|
|
108
|
+
sat: 80,
|
|
109
|
+
bodyL: 50,
|
|
110
|
+
trimL: 65,
|
|
111
|
+
accent: "#f1c40f",
|
|
112
|
+
skinL: 72,
|
|
113
|
+
},
|
|
114
|
+
medic: {
|
|
115
|
+
role: "medic",
|
|
116
|
+
hue: 0,
|
|
117
|
+
sat: 0,
|
|
118
|
+
bodyL: 90,
|
|
119
|
+
trimL: 95,
|
|
120
|
+
accent: "#e74c3c",
|
|
121
|
+
skinL: 72,
|
|
122
|
+
},
|
|
123
|
+
sonarTech: {
|
|
124
|
+
role: "sonarTech",
|
|
125
|
+
hue: 185,
|
|
126
|
+
sat: 70,
|
|
127
|
+
bodyL: 35,
|
|
128
|
+
trimL: 55,
|
|
129
|
+
accent: "#00d4ff",
|
|
130
|
+
skinL: 72,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/** Map stages to default crew roles */
|
|
135
|
+
export const STAGE_CREW_ROLE: Record<StageName, CrewRole> = {
|
|
136
|
+
intake: "sonarTech",
|
|
137
|
+
plan: "captain",
|
|
138
|
+
design: "navigator",
|
|
139
|
+
build: "engineer",
|
|
140
|
+
test: "engineer",
|
|
141
|
+
review: "medic",
|
|
142
|
+
compound_quality: "medic",
|
|
143
|
+
pr: "sonarTech",
|
|
144
|
+
merge: "operator",
|
|
145
|
+
deploy: "operator",
|
|
146
|
+
monitor: "navigator",
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/** Pick a crew role for a pipeline based on its current stage */
|
|
150
|
+
export function crewRoleForStage(stage: StageName): CrewRole {
|
|
151
|
+
return STAGE_CREW_ROLE[stage];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ── Nautical color palette ───────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
export const nautical = {
|
|
157
|
+
// Hull
|
|
158
|
+
hullOuter: "#1a2a3a",
|
|
159
|
+
hullInner: "#0f1d2d",
|
|
160
|
+
hullStroke: "#2a4a6a",
|
|
161
|
+
hullHighlight: "rgba(0, 212, 255, 0.08)",
|
|
162
|
+
|
|
163
|
+
// Ocean / background
|
|
164
|
+
oceanDeep: "#040810",
|
|
165
|
+
oceanMid: "#071018",
|
|
166
|
+
oceanLight: "#0a1828",
|
|
167
|
+
|
|
168
|
+
// Compartment
|
|
169
|
+
compartmentBg: "#0c1a2e",
|
|
170
|
+
compartmentBorder: "#1a3050",
|
|
171
|
+
compartmentActive: "rgba(0, 212, 255, 0.12)",
|
|
172
|
+
|
|
173
|
+
// Pipes
|
|
174
|
+
pipeColor: "#1a3050",
|
|
175
|
+
pipeFlow: "#00d4ff",
|
|
176
|
+
pipeFlowDim: "rgba(0, 212, 255, 0.3)",
|
|
177
|
+
|
|
178
|
+
// Porthole
|
|
179
|
+
portholeRing: "#2a4a6a",
|
|
180
|
+
portholeGlass: "rgba(0, 212, 255, 0.06)",
|
|
181
|
+
portholeGlow: "rgba(0, 212, 255, 0.15)",
|
|
182
|
+
|
|
183
|
+
// Effects
|
|
184
|
+
bubbleColor: "rgba(120, 200, 255, 0.4)",
|
|
185
|
+
sonarColor: "#00d4ff",
|
|
186
|
+
sparkleColor: "#ffffff",
|
|
187
|
+
|
|
188
|
+
// Depth gauge
|
|
189
|
+
depthGaugeBg: "#0a1628",
|
|
190
|
+
depthGaugeFill: "#00d4ff",
|
|
191
|
+
depthGaugeBorder: "#1a3050",
|
|
192
|
+
|
|
193
|
+
// Text
|
|
194
|
+
labelColor: "#8899b8",
|
|
195
|
+
labelActive: "#e8ecf4",
|
|
196
|
+
} as const;
|
|
197
|
+
|
|
198
|
+
// ── Layout constants ─────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
export const layout = {
|
|
201
|
+
/** Rows in the submarine */
|
|
202
|
+
rows: 3,
|
|
203
|
+
/** Max columns per row */
|
|
204
|
+
maxCols: 4,
|
|
205
|
+
/** Padding inside hull */
|
|
206
|
+
hullPadding: 24,
|
|
207
|
+
/** Gap between compartments */
|
|
208
|
+
compartmentGap: 12,
|
|
209
|
+
/** Compartment corner radius */
|
|
210
|
+
compartmentRadius: 6,
|
|
211
|
+
/** Hull corner radius */
|
|
212
|
+
hullRadius: 32,
|
|
213
|
+
/** Depth gauge width */
|
|
214
|
+
depthGaugeWidth: 24,
|
|
215
|
+
/** Crew manifest bar height */
|
|
216
|
+
manifestHeight: 36,
|
|
217
|
+
/** Min compartment size */
|
|
218
|
+
minCompartmentW: 100,
|
|
219
|
+
minCompartmentH: 70,
|
|
220
|
+
/** Pipe width */
|
|
221
|
+
pipeWidth: 2,
|
|
222
|
+
/** Porthole radius */
|
|
223
|
+
portholeRadius: 8,
|
|
224
|
+
} as const;
|
|
225
|
+
|
|
226
|
+
// ── Animation timing ─────────────────────────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
export const timing = {
|
|
229
|
+
/** Agent walk speed in pixels/second */
|
|
230
|
+
walkSpeed: 48,
|
|
231
|
+
/** Idle bob amplitude in pixels */
|
|
232
|
+
idleBobAmplitude: 1,
|
|
233
|
+
/** Idle bob period in seconds */
|
|
234
|
+
idleBobPeriod: 2,
|
|
235
|
+
/** Idle wander interval range [min, max] seconds */
|
|
236
|
+
idleWanderRange: [2, 4] as [number, number],
|
|
237
|
+
/** Working frame interval in seconds */
|
|
238
|
+
workFrameInterval: 0.3,
|
|
239
|
+
/** Alert flash interval in seconds */
|
|
240
|
+
alertFlashInterval: 0.4,
|
|
241
|
+
/** Spawn cascade duration in seconds */
|
|
242
|
+
spawnDuration: 0.5,
|
|
243
|
+
/** Despawn dissolve duration in seconds */
|
|
244
|
+
despawnDuration: 0.5,
|
|
245
|
+
/** Sonar ping ring expand duration */
|
|
246
|
+
sonarPingDuration: 1.5,
|
|
247
|
+
/** Sonar ping ring count */
|
|
248
|
+
sonarPingRings: 3,
|
|
249
|
+
/** Pipe flow dash speed (px/s) */
|
|
250
|
+
pipeFlowSpeed: 30,
|
|
251
|
+
/** Bubble rise speed (px/s) */
|
|
252
|
+
bubbleRiseSpeed: 15,
|
|
253
|
+
} as const;
|
package/dashboard/src/main.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { teamView } from "./views/team";
|
|
|
29
29
|
import { fleetMapView } from "./views/fleet-map";
|
|
30
30
|
import { pipelineTheaterView } from "./views/pipeline-theater";
|
|
31
31
|
import { agentCockpitView } from "./views/agent-cockpit";
|
|
32
|
+
import { shipyardView } from "./views/shipyard";
|
|
32
33
|
|
|
33
34
|
// Register all views
|
|
34
35
|
registerView("overview", overviewView);
|
|
@@ -43,6 +44,7 @@ registerView("team", teamView);
|
|
|
43
44
|
registerView("fleet-map", fleetMapView);
|
|
44
45
|
registerView("pipeline-theater", pipelineTheaterView);
|
|
45
46
|
registerView("agent-cockpit", agentCockpitView);
|
|
47
|
+
registerView("shipyard", shipyardView);
|
|
46
48
|
|
|
47
49
|
// Setup header (user menu, daemon control, emergency brake)
|
|
48
50
|
setupHeader();
|
|
@@ -255,6 +255,16 @@ export interface HeatmapData {
|
|
|
255
255
|
heatmap: Record<string, Record<string, number>>;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
export interface ContextEfficiency {
|
|
259
|
+
avg_utilization: number;
|
|
260
|
+
avg_trim_ratio: number;
|
|
261
|
+
total_raw_chars: number;
|
|
262
|
+
total_trimmed_chars: number;
|
|
263
|
+
total_discarded_chars: number;
|
|
264
|
+
trim_events: number;
|
|
265
|
+
total_iterations: number;
|
|
266
|
+
}
|
|
267
|
+
|
|
258
268
|
export interface DaemonConfig {
|
|
259
269
|
paused?: boolean;
|
|
260
270
|
config?: Record<string, unknown>;
|
|
@@ -328,7 +338,8 @@ export type TabId =
|
|
|
328
338
|
| "team"
|
|
329
339
|
| "fleet-map"
|
|
330
340
|
| "pipeline-theater"
|
|
331
|
-
| "agent-cockpit"
|
|
341
|
+
| "agent-cockpit"
|
|
342
|
+
| "shipyard";
|
|
332
343
|
|
|
333
344
|
export interface View {
|
|
334
345
|
init(): void;
|
|
@@ -76,8 +76,9 @@ function loadMoreActivity(): void {
|
|
|
76
76
|
.then((result) => {
|
|
77
77
|
const existing = store.get("activityEvents");
|
|
78
78
|
const newEvents = result.events || [];
|
|
79
|
+
const merged = [...existing, ...newEvents];
|
|
79
80
|
store.update({
|
|
80
|
-
activityEvents:
|
|
81
|
+
activityEvents: merged.length > 500 ? merged.slice(-500) : merged,
|
|
81
82
|
activityHasMore: result.hasMore || false,
|
|
82
83
|
activityOffset: existing.length + newEvents.length,
|
|
83
84
|
});
|