soja-cli 1.4.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/LICENSE +18 -0
- package/README.md +117 -0
- package/dist/application/chat.js +1 -0
- package/dist/application/filters.js +27 -0
- package/dist/application/import.js +1 -0
- package/dist/application/ports.js +1 -0
- package/dist/application/repository-root.js +18 -0
- package/dist/application/services/backup-service.js +93 -0
- package/dist/application/services/folder-service.js +116 -0
- package/dist/application/services/git-workflow-service.js +593 -0
- package/dist/application/services/index.js +24 -0
- package/dist/application/services/project-service.js +129 -0
- package/dist/application/services/session-service.js +52 -0
- package/dist/application/services/task-service.js +350 -0
- package/dist/application/services/update-service.js +60 -0
- package/dist/application/services/workspace-service.js +81 -0
- package/dist/application/timeline.js +83 -0
- package/dist/application/types.js +1 -0
- package/dist/application/validation.js +30 -0
- package/dist/bootstrap.js +91 -0
- package/dist/cli/commands/access.js +60 -0
- package/dist/cli/commands/account.js +203 -0
- package/dist/cli/commands/args.js +27 -0
- package/dist/cli/commands/backup.js +70 -0
- package/dist/cli/commands/chat.js +133 -0
- package/dist/cli/commands/dev.js +59 -0
- package/dist/cli/commands/folders.js +59 -0
- package/dist/cli/commands/git-ops.js +200 -0
- package/dist/cli/commands/import.js +48 -0
- package/dist/cli/commands/info.js +82 -0
- package/dist/cli/commands/project.js +108 -0
- package/dist/cli/commands/start.js +34 -0
- package/dist/cli/commands/sync.js +43 -0
- package/dist/cli/commands/task.js +309 -0
- package/dist/cli/commands/tui.js +43 -0
- package/dist/cli/commands/update.js +20 -0
- package/dist/cli/commands/workspace.js +55 -0
- package/dist/cli/index.js +103 -0
- package/dist/cli/output.js +56 -0
- package/dist/cli/prompt.js +28 -0
- package/dist/cli/runtime.js +43 -0
- package/dist/config/config.js +103 -0
- package/dist/config/credentials.js +68 -0
- package/dist/config/paths.js +18 -0
- package/dist/config/server.js +21 -0
- package/dist/config/version.js +7 -0
- package/dist/data/local/activity.js +33 -0
- package/dist/data/local/clock.js +1 -0
- package/dist/data/local/comments.js +27 -0
- package/dist/data/local/index.js +48 -0
- package/dist/data/local/projects.js +53 -0
- package/dist/data/local/tasks.js +104 -0
- package/dist/data/local/users.js +38 -0
- package/dist/data/local/workspaces.js +55 -0
- package/dist/data/remote/api-client.js +103 -0
- package/dist/data/remote/live.js +93 -0
- package/dist/data/repositories.js +1 -0
- package/dist/data/sync/chat.js +242 -0
- package/dist/data/sync/engine.js +459 -0
- package/dist/data/sync/importer.js +156 -0
- package/dist/data/sync/index.js +109 -0
- package/dist/data/sync/services.js +263 -0
- package/dist/data/sync/store.js +200 -0
- package/dist/database/client.js +76 -0
- package/dist/database/migrate.js +27 -0
- package/dist/database/migrations/0000_init.sql +100 -0
- package/dist/database/migrations/0001_git-operations.sql +19 -0
- package/dist/database/migrations/0002_merge-detection.sql +19 -0
- package/dist/database/migrations/0003_sync-replica.sql +40 -0
- package/dist/database/migrations/0004_chat-replica.sql +52 -0
- package/dist/database/migrations/0005_pr-events.sql +18 -0
- package/dist/database/migrations/0006_archive.sql +20 -0
- package/dist/database/migrations/meta/0000_snapshot.json +710 -0
- package/dist/database/migrations/meta/0001_snapshot.json +717 -0
- package/dist/database/migrations/meta/0002_snapshot.json +724 -0
- package/dist/database/migrations/meta/0003_snapshot.json +972 -0
- package/dist/database/migrations/meta/0004_snapshot.json +1168 -0
- package/dist/database/migrations/meta/0005_snapshot.json +1168 -0
- package/dist/database/migrations/meta/0006_snapshot.json +1183 -0
- package/dist/database/migrations/meta/_journal.json +55 -0
- package/dist/database/schema.js +170 -0
- package/dist/dev/seed.js +84 -0
- package/dist/domain/activity.js +22 -0
- package/dist/domain/chat.js +21 -0
- package/dist/domain/entities.js +1 -0
- package/dist/domain/errors.js +23 -0
- package/dist/domain/naming.js +33 -0
- package/dist/domain/task.js +140 -0
- package/dist/domain/workflow.js +23 -0
- package/dist/git/cli-git.js +363 -0
- package/dist/git/console.js +37 -0
- package/dist/git/diagnose.js +145 -0
- package/dist/git/merge-evidence.js +23 -0
- package/dist/git/releases.js +95 -0
- package/dist/git/types.js +16 -0
- package/dist/ui/App.js +71 -0
- package/dist/ui/Shell.js +211 -0
- package/dist/ui/app-state.js +213 -0
- package/dist/ui/branding/CompactLogo.js +8 -0
- package/dist/ui/branding/Logo.js +22 -0
- package/dist/ui/branding/Splash.js +21 -0
- package/dist/ui/branding/brand.js +23 -0
- package/dist/ui/chrome/Sidebar.js +50 -0
- package/dist/ui/chrome/StatusBar.js +26 -0
- package/dist/ui/chrome/Toast.js +11 -0
- package/dist/ui/chrome/TopBar.js +10 -0
- package/dist/ui/chrome/context.js +33 -0
- package/dist/ui/components/ConsoleLines.js +16 -0
- package/dist/ui/components/EmptyState.js +8 -0
- package/dist/ui/components/Labels.js +25 -0
- package/dist/ui/components/PullRequestLabel.js +27 -0
- package/dist/ui/components/TaskTable.js +41 -0
- package/dist/ui/components/TextField.js +11 -0
- package/dist/ui/components/TextInput.js +29 -0
- package/dist/ui/components/task-columns.js +27 -0
- package/dist/ui/copy.js +23 -0
- package/dist/ui/hooks/use-commands.js +58 -0
- package/dist/ui/hooks/use-flows.js +254 -0
- package/dist/ui/hooks/use-layout.js +36 -0
- package/dist/ui/hooks/use-list.js +28 -0
- package/dist/ui/hooks/use-measure.js +22 -0
- package/dist/ui/hooks/use-query.js +29 -0
- package/dist/ui/hooks/use-task-actions.js +450 -0
- package/dist/ui/hooks/use-terminal-size.js +19 -0
- package/dist/ui/input/KeyProvider.js +101 -0
- package/dist/ui/input/dispatcher.js +26 -0
- package/dist/ui/input/list-navigation.js +38 -0
- package/dist/ui/input/mouse.js +78 -0
- package/dist/ui/input/text-editing.js +62 -0
- package/dist/ui/kit/Badge.js +8 -0
- package/dist/ui/kit/Button.js +13 -0
- package/dist/ui/kit/Clickable.js +14 -0
- package/dist/ui/kit/Gauge.js +9 -0
- package/dist/ui/kit/Keycap.js +7 -0
- package/dist/ui/kit/Panel.js +30 -0
- package/dist/ui/kit/ScrollBar.js +14 -0
- package/dist/ui/kit/Spinner.js +9 -0
- package/dist/ui/kit/Tabs.js +12 -0
- package/dist/ui/mascot/AgentScene.js +30 -0
- package/dist/ui/mascot/Mascot.js +26 -0
- package/dist/ui/mascot/ansi.js +25 -0
- package/dist/ui/mascot/engine.js +146 -0
- package/dist/ui/mascot/index.js +6 -0
- package/dist/ui/mascot/palette.js +18 -0
- package/dist/ui/mascot/scenes/bugHunt.js +115 -0
- package/dist/ui/mascot/scenes/celebrate.js +76 -0
- package/dist/ui/mascot/scenes/cleaning.js +156 -0
- package/dist/ui/mascot/scenes/clockIn.js +105 -0
- package/dist/ui/mascot/scenes/deadline.js +125 -0
- package/dist/ui/mascot/scenes/empty.js +149 -0
- package/dist/ui/mascot/scenes/fire.js +138 -0
- package/dist/ui/mascot/scenes/index.js +25 -0
- package/dist/ui/mascot/scenes/planning.js +73 -0
- package/dist/ui/mascot/scenes/resting.js +90 -0
- package/dist/ui/mascot/scenes/syncing.js +116 -0
- package/dist/ui/mascot/scenes/working.js +193 -0
- package/dist/ui/mascot/selection.js +32 -0
- package/dist/ui/mascot/states.js +25 -0
- package/dist/ui/navigation/routes.js +17 -0
- package/dist/ui/overlays/CommitOverlay.js +93 -0
- package/dist/ui/overlays/ConfirmOverlay.js +42 -0
- package/dist/ui/overlays/GitLogOverlay.js +22 -0
- package/dist/ui/overlays/GitRunOverlay.js +94 -0
- package/dist/ui/overlays/NewTaskOverlay.js +117 -0
- package/dist/ui/overlays/OverlayFrame.js +35 -0
- package/dist/ui/overlays/PickerOverlay.js +72 -0
- package/dist/ui/overlays/PromptOverlay.js +51 -0
- package/dist/ui/overlays/SearchOverlay.js +49 -0
- package/dist/ui/overlays/options.js +29 -0
- package/dist/ui/overlays/types.js +1 -0
- package/dist/ui/screens/AccessScreen.js +91 -0
- package/dist/ui/screens/ChatScreen.js +450 -0
- package/dist/ui/screens/HelpScreen.js +99 -0
- package/dist/ui/screens/ProjectsScreen.js +66 -0
- package/dist/ui/screens/ScreenFrame.js +14 -0
- package/dist/ui/screens/SetupScreen.js +79 -0
- package/dist/ui/screens/TaskListScreen.js +152 -0
- package/dist/ui/screens/TaskScreen.js +193 -0
- package/dist/ui/screens/WelcomeScreen.js +254 -0
- package/dist/ui/screens/WorkspacesScreen.js +64 -0
- package/dist/ui/theme/detect.js +59 -0
- package/dist/ui/theme/theme.js +135 -0
- package/dist/utils/editor.js +37 -0
- package/dist/utils/errors.js +23 -0
- package/dist/utils/text.js +60 -0
- package/dist/utils/time.js +25 -0
- package/docs/NPM_PUBLISH.md +35 -0
- package/docs/PRIVACY.md +38 -0
- package/package.json +74 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion -- canvas operations validate coordinates before writing. */
|
|
2
|
+
/**
|
|
3
|
+
* Motor de escenas ASCII, sin dependencias.
|
|
4
|
+
* Una escena dibuja en un Canvas de celdas (carácter + tono) y el renderer
|
|
5
|
+
* (Ink, ANSI o navegador) traduce cada tono a un color.
|
|
6
|
+
*/
|
|
7
|
+
export class Canvas {
|
|
8
|
+
width;
|
|
9
|
+
height;
|
|
10
|
+
chars;
|
|
11
|
+
tones;
|
|
12
|
+
constructor(width, height) {
|
|
13
|
+
this.width = width;
|
|
14
|
+
this.height = height;
|
|
15
|
+
this.chars = new Array(width * height).fill(' ');
|
|
16
|
+
this.tones = new Array(width * height).fill(null);
|
|
17
|
+
}
|
|
18
|
+
/** Escribe un texto; los espacios son transparentes. */
|
|
19
|
+
put(x, y, text, tone = null) {
|
|
20
|
+
const rx = Math.round(x);
|
|
21
|
+
const ry = Math.round(y);
|
|
22
|
+
if (ry < 0 || ry >= this.height)
|
|
23
|
+
return;
|
|
24
|
+
for (let i = 0; i < text.length; i++) {
|
|
25
|
+
const ch = text[i];
|
|
26
|
+
const cx = rx + i;
|
|
27
|
+
if (ch === ' ' || cx < 0 || cx >= this.width)
|
|
28
|
+
continue;
|
|
29
|
+
this.chars[ry * this.width + cx] = ch;
|
|
30
|
+
this.tones[ry * this.width + cx] = tone;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Escribe un solo carácter, aunque sea un espacio (sirve para borrar). */
|
|
34
|
+
set(x, y, ch, tone = null) {
|
|
35
|
+
const rx = Math.round(x);
|
|
36
|
+
const ry = Math.round(y);
|
|
37
|
+
if (rx < 0 || rx >= this.width || ry < 0 || ry >= this.height)
|
|
38
|
+
return;
|
|
39
|
+
this.chars[ry * this.width + rx] = ch;
|
|
40
|
+
this.tones[ry * this.width + rx] = tone;
|
|
41
|
+
}
|
|
42
|
+
/** Dibuja un sprite de varias líneas con la parte superior en `top`. */
|
|
43
|
+
sprite(x, top, lines, tone) {
|
|
44
|
+
lines.forEach((line, i) => this.put(x, top + i, line, tone));
|
|
45
|
+
}
|
|
46
|
+
/** Filas agrupadas en segmentos del mismo tono, listas para renderizar. */
|
|
47
|
+
rows() {
|
|
48
|
+
const out = [];
|
|
49
|
+
for (let y = 0; y < this.height; y++) {
|
|
50
|
+
const row = [];
|
|
51
|
+
let tone;
|
|
52
|
+
let buf = '';
|
|
53
|
+
for (let x = 0; x < this.width; x++) {
|
|
54
|
+
const i = y * this.width + x;
|
|
55
|
+
const t = this.chars[i] === ' ' ? null : this.tones[i];
|
|
56
|
+
if (t !== tone && buf) {
|
|
57
|
+
row.push({ text: buf, tone: tone ?? null });
|
|
58
|
+
buf = '';
|
|
59
|
+
}
|
|
60
|
+
tone = t;
|
|
61
|
+
buf += this.chars[i];
|
|
62
|
+
}
|
|
63
|
+
if (buf)
|
|
64
|
+
row.push({ text: buf, tone: tone ?? null });
|
|
65
|
+
out.push(row);
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
toString() {
|
|
70
|
+
const lines = [];
|
|
71
|
+
for (let y = 0; y < this.height; y++) {
|
|
72
|
+
lines.push(this.chars.slice(y * this.width, (y + 1) * this.width).join(''));
|
|
73
|
+
}
|
|
74
|
+
return lines.join('\n');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/* ---------- utilidades compartidas ---------- */
|
|
78
|
+
/** PRNG determinista (mulberry32) para que cada escena sea reproducible. */
|
|
79
|
+
export function rng(seed = 1) {
|
|
80
|
+
let a = seed >>> 0;
|
|
81
|
+
return () => {
|
|
82
|
+
a = (a + 0x6d2b79f5) >>> 0;
|
|
83
|
+
let t = a;
|
|
84
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
85
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
86
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const MIRROR = {
|
|
90
|
+
'/': '\\',
|
|
91
|
+
'\\': '/',
|
|
92
|
+
'[': ']',
|
|
93
|
+
']': '[',
|
|
94
|
+
'(': ')',
|
|
95
|
+
')': '(',
|
|
96
|
+
'<': '>',
|
|
97
|
+
'>': '<',
|
|
98
|
+
};
|
|
99
|
+
/** Voltea un sprite horizontalmente. */
|
|
100
|
+
export function mirror(lines) {
|
|
101
|
+
const w = Math.max(...lines.map((l) => l.length));
|
|
102
|
+
return lines.map((l) => l
|
|
103
|
+
.padEnd(w)
|
|
104
|
+
.split('')
|
|
105
|
+
.reverse()
|
|
106
|
+
.map((c) => MIRROR[c] ?? c)
|
|
107
|
+
.join(''));
|
|
108
|
+
}
|
|
109
|
+
/** Sustituye la cara `o.o` del sprite. */
|
|
110
|
+
export function withFace(lines, face) {
|
|
111
|
+
return lines.map((l) => l.replace('o.o', face));
|
|
112
|
+
}
|
|
113
|
+
/** Cara con parpadeo ocasional, desfasado por agente. */
|
|
114
|
+
export function blink(t, i, face = 'o.o') {
|
|
115
|
+
return (t + i * 37) % 70 < 2 ? '-.-' : face;
|
|
116
|
+
}
|
|
117
|
+
export function soilLine(width, r) {
|
|
118
|
+
let s = '';
|
|
119
|
+
for (let i = 0; i < width; i++) {
|
|
120
|
+
const q = r();
|
|
121
|
+
s += q < 0.08 ? '.' : q < 0.12 ? ':' : q < 0.15 ? "'" : ' ';
|
|
122
|
+
}
|
|
123
|
+
return s;
|
|
124
|
+
}
|
|
125
|
+
export function drawGround(c, groundY, soil) {
|
|
126
|
+
for (let x = 0; x < c.width; x++)
|
|
127
|
+
c.set(x, groundY, '▀', 'soil');
|
|
128
|
+
soil.forEach((line, i) => c.put(0, groundY + 1 + i, line, 'soil'));
|
|
129
|
+
}
|
|
130
|
+
export function drawCloud(c, t, speed, offset, y, big = true) {
|
|
131
|
+
const x = ((t * speed + offset) % (c.width + 14)) - 12;
|
|
132
|
+
if (big) {
|
|
133
|
+
c.put(x, y, ' .--.', 'cloud');
|
|
134
|
+
c.put(x, y + 1, '.( ).', 'cloud');
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
c.put(x, y, ' .-. ', 'cloud');
|
|
138
|
+
c.put(x, y + 1, '( )', 'cloud');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Sprites base del agente (5 de ancho, cara `o.o`). */
|
|
142
|
+
export const AGENT = {
|
|
143
|
+
stand: [' .-. ', '(o.o)', '( )', " ' ' "],
|
|
144
|
+
walk: [' .-. ', '(o.o)', '( )', " ' '"],
|
|
145
|
+
sit: [' .-. ', '(o.o)', '(___)'],
|
|
146
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AgentScene } from './AgentScene.js';
|
|
2
|
+
export { SCENES, SCENE_IDS } from './scenes/index.js';
|
|
3
|
+
export { TASK_STATE_SCENES, sceneForState } from './states.js';
|
|
4
|
+
export { defaultPalette } from './palette.js';
|
|
5
|
+
export { frameToAnsi, frameToText } from './ansi.js';
|
|
6
|
+
export { Canvas } from './engine.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const defaultPalette = {
|
|
2
|
+
agent: '#D85A30',
|
|
3
|
+
rock: '#9A9893',
|
|
4
|
+
soil: '#6B6A66',
|
|
5
|
+
dirt: '#C8913A',
|
|
6
|
+
spark: '#F2C14E',
|
|
7
|
+
fire: '#E24B4A',
|
|
8
|
+
flame: '#F2A93B',
|
|
9
|
+
water: '#4FA3E0',
|
|
10
|
+
good: '#63B34A',
|
|
11
|
+
bad: '#E24B4A',
|
|
12
|
+
accent: '#5AA9E6',
|
|
13
|
+
cloud: '#7D7C77',
|
|
14
|
+
wood: '#A9743F',
|
|
15
|
+
paper: '#E8E6DF',
|
|
16
|
+
leaf: '#5FAE54',
|
|
17
|
+
text: '#CFCDC6',
|
|
18
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { blink, drawGround, mirror, rng, soilLine, withFace } from '../engine.js';
|
|
2
|
+
const W = 78;
|
|
3
|
+
const H = 12;
|
|
4
|
+
const G = 10;
|
|
5
|
+
const BUG_A = [' \\ / ', '=(oo)=', ' / \\ '];
|
|
6
|
+
const BUG_B = [' / \\ ', '=(oo)=', ' \\ / '];
|
|
7
|
+
const NET_UP = [' .-. (@)', '(o.o)/ ', '( ) ', " ' ' "];
|
|
8
|
+
const NET_DOWN = [' .-. ', '(o.o) ', '( )\\ ', " ' ' (@)"];
|
|
9
|
+
const MAGNIFY = [' .-. ', '(o.o) ', '( )-O', " ' ' "];
|
|
10
|
+
const SCARED = ['\\.-./', '(O.O)', '( )', " ' ' "];
|
|
11
|
+
const CRATE_X = 36;
|
|
12
|
+
export const bugHunt = {
|
|
13
|
+
id: 'bugHunt',
|
|
14
|
+
title: 'Cazando bugs',
|
|
15
|
+
useFor: 'Errores / tareas bloqueadas',
|
|
16
|
+
width: W,
|
|
17
|
+
height: H,
|
|
18
|
+
create(seed = 8) {
|
|
19
|
+
const r = rng(seed);
|
|
20
|
+
const soil = [soilLine(W, r)];
|
|
21
|
+
let t = 0;
|
|
22
|
+
let bx = 60;
|
|
23
|
+
let bv = -0.7;
|
|
24
|
+
let netX = 10;
|
|
25
|
+
let magX = 2;
|
|
26
|
+
let swing = 0;
|
|
27
|
+
let caught = 0;
|
|
28
|
+
let respawn = 0;
|
|
29
|
+
let caughtFlash = 0;
|
|
30
|
+
let dust = [];
|
|
31
|
+
return {
|
|
32
|
+
step() {
|
|
33
|
+
t++;
|
|
34
|
+
if (respawn) {
|
|
35
|
+
respawn--;
|
|
36
|
+
if (!respawn) {
|
|
37
|
+
bx = r() < 0.5 ? 2 : W - 8;
|
|
38
|
+
bv = bx < 10 ? 0.7 : -0.7;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
if (r() < 0.04)
|
|
43
|
+
bv = -bv;
|
|
44
|
+
if (r() < 0.02)
|
|
45
|
+
bv *= 1.8;
|
|
46
|
+
bv = Math.max(-1.3, Math.min(1.3, bv));
|
|
47
|
+
if (Math.abs(bv) > 0.8 && r() < 0.1)
|
|
48
|
+
bv *= 0.6;
|
|
49
|
+
bx += bv;
|
|
50
|
+
if (bx < 1 || bx > W - 7) {
|
|
51
|
+
bv = -bv;
|
|
52
|
+
bx = Math.max(1, Math.min(W - 7, bx));
|
|
53
|
+
}
|
|
54
|
+
if (t % 3 === 0)
|
|
55
|
+
dust.push({ x: bx + (bv > 0 ? 0 : 5), y: G - 1, vx: -bv * 0.3, vy: -0.1, ch: '.', tone: 'soil', life: 0 });
|
|
56
|
+
}
|
|
57
|
+
const target = bx + 3;
|
|
58
|
+
const netFacesRight = target >= netX + 3;
|
|
59
|
+
const goal = netFacesRight ? target - 6 : target;
|
|
60
|
+
netX += Math.sign(goal - netX) * Math.min(0.6, Math.abs(goal - netX));
|
|
61
|
+
magX += Math.sign(netX - 9 - magX) * Math.min(0.35, Math.abs(netX - 9 - magX));
|
|
62
|
+
if (swing)
|
|
63
|
+
swing--;
|
|
64
|
+
else if (!respawn && Math.abs(goal - netX) < 3 && t % 8 === 0)
|
|
65
|
+
swing = 6;
|
|
66
|
+
if (swing === 3 && !respawn) {
|
|
67
|
+
const netPoint = netFacesRight ? netX + 6 : netX - 1;
|
|
68
|
+
if (Math.abs(netPoint - target) < 2.5) {
|
|
69
|
+
caught++;
|
|
70
|
+
respawn = 30;
|
|
71
|
+
caughtFlash = 14;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (caughtFlash)
|
|
75
|
+
caughtFlash--;
|
|
76
|
+
for (const d of dust) {
|
|
77
|
+
d.x += d.vx;
|
|
78
|
+
d.y += d.vy;
|
|
79
|
+
d.life = (d.life ?? 0) + 1;
|
|
80
|
+
}
|
|
81
|
+
dust = dust.filter((d) => (d.life ?? 0) < 6);
|
|
82
|
+
},
|
|
83
|
+
draw(c) {
|
|
84
|
+
drawGround(c, G, soil);
|
|
85
|
+
c.put(CRATE_X - 1, G - 2, '[_____]', 'wood');
|
|
86
|
+
c.put(CRATE_X - 1, G - 1, '[_____]', 'wood');
|
|
87
|
+
const near = !respawn && Math.abs(bx + 3 - (CRATE_X + 2)) < 10;
|
|
88
|
+
c.sprite(CRATE_X, G - 6, near ? SCARED : withFace([' .-. ', '(o.o)', '( )', " ' ' "], blink(t, 3)), 'agent');
|
|
89
|
+
if (near && t % 8 < 5)
|
|
90
|
+
c.put(CRATE_X + 6, G - 7, '(!!)', 'cloud');
|
|
91
|
+
if (!respawn) {
|
|
92
|
+
c.sprite(bx, G - 3, Math.floor(t / 2) % 2 ? BUG_A : BUG_B, 'leaf');
|
|
93
|
+
}
|
|
94
|
+
for (const d of dust)
|
|
95
|
+
c.put(d.x, d.y, d.ch, d.tone);
|
|
96
|
+
const right = bx + 3 >= netX + 3;
|
|
97
|
+
let net = swing > 3 ? NET_UP : swing > 0 ? NET_DOWN : NET_UP;
|
|
98
|
+
if (!right)
|
|
99
|
+
net = mirror(net);
|
|
100
|
+
const nx = right ? netX : netX - 3;
|
|
101
|
+
c.sprite(nx, G - 4, withFace(net, caughtFlash ? '^.^' : '>.<'), 'agent');
|
|
102
|
+
if (caughtFlash) {
|
|
103
|
+
const px = right ? nx + 5 : nx;
|
|
104
|
+
c.put(px, G - 5, '(@)', 'leaf');
|
|
105
|
+
if (t % 6 < 4)
|
|
106
|
+
c.put(nx - 1, G - 6, '(te tengo!)', 'good');
|
|
107
|
+
}
|
|
108
|
+
c.sprite(magX, G - 4, withFace(MAGNIFY, blink(t, 5, 'o.O')), 'agent');
|
|
109
|
+
},
|
|
110
|
+
status() {
|
|
111
|
+
return respawn ? `bug atrapado · total ${caught}` : `persiguiendo bug · atrapados ${caught}`;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion -- scene arrays are indexed within their declared bounds. */
|
|
2
|
+
import { AGENT, blink, drawGround, mirror, rng, soilLine, withFace } from '../engine.js';
|
|
3
|
+
const W = 78;
|
|
4
|
+
const H = 13;
|
|
5
|
+
const G = 11;
|
|
6
|
+
const JUMP = ['\\.-./', '(^.^)', '( )', " / \\ "];
|
|
7
|
+
const CHEER = [' .-. ', '(^.^)', '( )/', " ' ' "];
|
|
8
|
+
const BOX = [" .----------. ", " | | ", " | // | ", " | \\\\ // | ", " | \\\\ // | ", " | \\V/ | ", " '----------' "];
|
|
9
|
+
const CONFETTI = ['*', '+', '.', 'o', '~', "'"];
|
|
10
|
+
const TONES = ['agent', 'accent', 'good', 'spark', 'water', 'flame'];
|
|
11
|
+
export const celebrate = {
|
|
12
|
+
id: 'celebrate',
|
|
13
|
+
title: 'Celebrando',
|
|
14
|
+
useFor: 'Tarea completada / meta alcanzada',
|
|
15
|
+
width: W,
|
|
16
|
+
height: H,
|
|
17
|
+
create(seed = 6) {
|
|
18
|
+
const r = rng(seed);
|
|
19
|
+
const soil = [soilLine(W, r)];
|
|
20
|
+
const agents = [3, 12, 21, 52, 61, 70].map((x, i) => ({ x, off: i * 3, m: i >= 3 }));
|
|
21
|
+
let t = 0;
|
|
22
|
+
let streak = 1;
|
|
23
|
+
let confetti = [];
|
|
24
|
+
let bursts = [];
|
|
25
|
+
return {
|
|
26
|
+
step() {
|
|
27
|
+
t++;
|
|
28
|
+
for (let i = 0; i < 2; i++) {
|
|
29
|
+
confetti.push({ x: r() * W, y: -1, vx: (r() - 0.5) * 0.3, vy: 0.15 + r() * 0.2, ch: CONFETTI[Math.floor(r() * CONFETTI.length)], tone: TONES[Math.floor(r() * TONES.length)] });
|
|
30
|
+
}
|
|
31
|
+
for (const p of confetti) {
|
|
32
|
+
p.x += p.vx + Math.sin((t + p.y * 3) * 0.3) * 0.15;
|
|
33
|
+
p.y += p.vy;
|
|
34
|
+
}
|
|
35
|
+
confetti = confetti.filter((p) => p.y < G);
|
|
36
|
+
if (t % 18 === 0) {
|
|
37
|
+
bursts.push({ x: 8 + r() * (W - 16), y: 1 + r() * 3, l: 0, tone: TONES[Math.floor(r() * TONES.length)] });
|
|
38
|
+
if (t % 90 === 0)
|
|
39
|
+
streak++;
|
|
40
|
+
}
|
|
41
|
+
for (const b of bursts)
|
|
42
|
+
b.l++;
|
|
43
|
+
bursts = bursts.filter((b) => b.l < 8);
|
|
44
|
+
},
|
|
45
|
+
draw(c) {
|
|
46
|
+
drawGround(c, G, soil);
|
|
47
|
+
for (const b of bursts) {
|
|
48
|
+
const rad = b.l * 0.9;
|
|
49
|
+
const ch = b.l < 3 ? '*' : b.l < 6 ? '+' : '.';
|
|
50
|
+
for (let a = 0; a < 8; a++) {
|
|
51
|
+
const ang = (a / 8) * Math.PI * 2;
|
|
52
|
+
c.put(b.x + Math.cos(ang) * rad * 2, b.y + Math.sin(ang) * rad * 0.8, ch, b.tone);
|
|
53
|
+
}
|
|
54
|
+
if (b.l < 2)
|
|
55
|
+
c.put(b.x, b.y, 'o', 'spark');
|
|
56
|
+
}
|
|
57
|
+
const pulse = Math.floor(t / 6) % 2;
|
|
58
|
+
c.sprite(32, G - 7, BOX, 'rock');
|
|
59
|
+
c.sprite(32, G - 7, BOX.map((l) => l.replace(/[.\-|']/g, ' ')), pulse ? 'good' : 'leaf');
|
|
60
|
+
agents.forEach((a, i) => {
|
|
61
|
+
const ph = (t + a.off) % 12;
|
|
62
|
+
const up = ph < 6 ? Math.round(Math.sin((ph / 6) * Math.PI) * 2) : 0;
|
|
63
|
+
let sp = up > 0 ? JUMP : i % 2 ? CHEER : withFace(AGENT.stand, '^.^');
|
|
64
|
+
if (a.m)
|
|
65
|
+
sp = mirror(sp);
|
|
66
|
+
c.sprite(a.x, G - 4 - up, withFace(sp, blink(t, i, '^.^')), 'agent');
|
|
67
|
+
});
|
|
68
|
+
for (const p of confetti)
|
|
69
|
+
c.put(p.x, p.y, p.ch, p.tone);
|
|
70
|
+
},
|
|
71
|
+
status() {
|
|
72
|
+
return `tarea completada · racha x${streak}`;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/* eslint-disable prefer-const -- particle arrays are mutated in place by the scene engine. */
|
|
2
|
+
import { AGENT, blink, drawGround, mirror, rng, soilLine, withFace } from '../engine.js';
|
|
3
|
+
const W = 78;
|
|
4
|
+
const H = 12;
|
|
5
|
+
const G = 10;
|
|
6
|
+
const CABINET = [' .------. ', ' |[ -- ]| ', ' |[ -- ]| ', ' |[ -- ]| ', " '------' "];
|
|
7
|
+
const BIN = ['._____.', '| |', '| |', "'-----'"];
|
|
8
|
+
const SWEEP_A = [' .-. ', '(o.o) ', '( )\\ ', " ' ' #"];
|
|
9
|
+
const SWEEP_B = [' .-. ', '(o.o) ', '( )| ', " ' ' #"];
|
|
10
|
+
const BROOM_UP = mirror([' .-. #', '(o.o)|', '( )|', " ' ' "]);
|
|
11
|
+
const CARRY = [' .-. ', '(o.o) ', '( )=', " ' ' "];
|
|
12
|
+
const CARRY_B = [' .-. ', '(o.o) ', '( )=', " ' ' "];
|
|
13
|
+
const SWEEP_L = 22;
|
|
14
|
+
const BIN_X = 70;
|
|
15
|
+
const BOX_X = 16;
|
|
16
|
+
const BOX_MAX = 6;
|
|
17
|
+
export const cleaning = {
|
|
18
|
+
id: 'cleaning',
|
|
19
|
+
title: 'Limpiando',
|
|
20
|
+
useFor: 'Archivar / limpiar tareas completadas',
|
|
21
|
+
width: W,
|
|
22
|
+
height: H,
|
|
23
|
+
create(seed = 11) {
|
|
24
|
+
const r = rng(seed);
|
|
25
|
+
const soil = [soilLine(W, r)];
|
|
26
|
+
let t = 0;
|
|
27
|
+
let dust = new Set();
|
|
28
|
+
const sprinkle = (n) => {
|
|
29
|
+
for (let i = 0; i < n; i++)
|
|
30
|
+
dust.add(SWEEP_L + 6 + Math.floor(r() * (BIN_X - SWEEP_L - 8)));
|
|
31
|
+
};
|
|
32
|
+
sprinkle(18);
|
|
33
|
+
const sweepers = [
|
|
34
|
+
{ x: SWEEP_L, dir: 1, load: 0, n: 1 },
|
|
35
|
+
{ x: SWEEP_L + 20, dir: 1, load: 0, n: 2 },
|
|
36
|
+
];
|
|
37
|
+
let bin = 0;
|
|
38
|
+
let archived = 0;
|
|
39
|
+
let boxes = BOX_MAX;
|
|
40
|
+
const arch = { x: BOX_X - 5, st: 'pick', k: 0 };
|
|
41
|
+
let puffs = [];
|
|
42
|
+
return {
|
|
43
|
+
step() {
|
|
44
|
+
t++;
|
|
45
|
+
for (const s of sweepers) {
|
|
46
|
+
if (s.dir > 0) {
|
|
47
|
+
s.x += 0.3;
|
|
48
|
+
const head = Math.round(s.x + 6);
|
|
49
|
+
for (const d of [head, head + 1]) {
|
|
50
|
+
if (dust.has(d)) {
|
|
51
|
+
dust.delete(d);
|
|
52
|
+
s.load++;
|
|
53
|
+
puffs.push({ x: d, y: G - 2, vx: (r() - 0.3) * 0.4, vy: -0.3 - r() * 0.2, ch: r() < 0.5 ? '.' : "'", tone: 'soil', life: 0 });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (t % 5 === s.n)
|
|
57
|
+
puffs.push({ x: s.x + 6, y: G - 1, vx: r() * 0.4, vy: -0.25, ch: '.', tone: 'cloud', life: 0 });
|
|
58
|
+
if (s.x + 7 >= BIN_X) {
|
|
59
|
+
bin = Math.min(100, bin + s.load * 4);
|
|
60
|
+
s.load = 0;
|
|
61
|
+
s.dir = -1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
s.x -= 0.45;
|
|
66
|
+
if (s.x <= SWEEP_L) {
|
|
67
|
+
s.dir = 1;
|
|
68
|
+
sprinkle(5);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (bin >= 100 && t % 40 === 0)
|
|
73
|
+
bin = 0;
|
|
74
|
+
arch.k++;
|
|
75
|
+
if (arch.st === 'pick' && arch.k > 5) {
|
|
76
|
+
if (boxes > 0) {
|
|
77
|
+
boxes--;
|
|
78
|
+
arch.st = 'go';
|
|
79
|
+
}
|
|
80
|
+
arch.k = 0;
|
|
81
|
+
}
|
|
82
|
+
else if (arch.st === 'go') {
|
|
83
|
+
arch.x -= 0.4;
|
|
84
|
+
if (arch.x <= 9) {
|
|
85
|
+
arch.st = 'drop';
|
|
86
|
+
arch.k = 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if (arch.st === 'drop' && arch.k > 5) {
|
|
90
|
+
archived++;
|
|
91
|
+
arch.st = 'back';
|
|
92
|
+
arch.k = 0;
|
|
93
|
+
}
|
|
94
|
+
else if (arch.st === 'back') {
|
|
95
|
+
arch.x += 0.4;
|
|
96
|
+
if (arch.x >= BOX_X - 5) {
|
|
97
|
+
arch.st = 'pick';
|
|
98
|
+
arch.k = 0;
|
|
99
|
+
if (boxes === 0)
|
|
100
|
+
boxes = BOX_MAX;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const p of puffs) {
|
|
104
|
+
p.x += p.vx;
|
|
105
|
+
p.y += p.vy;
|
|
106
|
+
p.life = (p.life ?? 0) + 1;
|
|
107
|
+
}
|
|
108
|
+
puffs = puffs.filter((p) => (p.life ?? 0) < 7);
|
|
109
|
+
},
|
|
110
|
+
draw(c) {
|
|
111
|
+
drawGround(c, G, soil);
|
|
112
|
+
c.sprite(0, G - 5, CABINET, 'rock');
|
|
113
|
+
const drawer = arch.st === 'drop' ? 1 : -1;
|
|
114
|
+
if (drawer > 0)
|
|
115
|
+
c.put(2, G - 3, '[====]', 'paper');
|
|
116
|
+
for (let i = 0; i < boxes; i++)
|
|
117
|
+
c.put(BOX_X + (i % 2) * 3, G - 1 - Math.floor(i / 2), '[▤]', 'paper');
|
|
118
|
+
c.sprite(BIN_X, G - 4, BIN, 'rock');
|
|
119
|
+
const fill = Math.round((bin / 100) * 2);
|
|
120
|
+
for (let i = 0; i < fill; i++)
|
|
121
|
+
c.put(BIN_X + 1, G - 2 - i, ':.:.:', 'soil');
|
|
122
|
+
if (bin >= 100 && t % 8 < 5)
|
|
123
|
+
c.put(BIN_X - 1, G - 6, '(lleno)', 'cloud');
|
|
124
|
+
for (const d of dust)
|
|
125
|
+
c.put(d, G - 1, t % 20 < 10 ? '.' : ',', 'soil');
|
|
126
|
+
sweepers.forEach((s, i) => {
|
|
127
|
+
if (s.dir > 0) {
|
|
128
|
+
const sp = Math.floor(t / 3) % 2 ? SWEEP_A : SWEEP_B;
|
|
129
|
+
c.sprite(s.x, G - 4, withFace(sp, blink(t, i)), 'agent');
|
|
130
|
+
if (s.load > 0)
|
|
131
|
+
c.put(s.x + 7, G - 1, s.load > 4 ? '::' : ':', 'soil');
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
c.sprite(s.x, G - 4, withFace(BROOM_UP, Math.floor(t / 2) % 2 ? '^.^' : blink(t, i, '^.^')), 'agent');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
const w = Math.floor(t / 2) % 2;
|
|
138
|
+
if (arch.st === 'go') {
|
|
139
|
+
c.sprite(arch.x, G - 4, withFace(mirror(w ? CARRY : CARRY_B), blink(t, 5)), 'agent');
|
|
140
|
+
c.put(arch.x - 3, G - 2, '[▤]', 'paper');
|
|
141
|
+
}
|
|
142
|
+
else if (arch.st === 'drop') {
|
|
143
|
+
c.sprite(arch.x, G - 4, withFace(mirror(CARRY), '^.^'), 'agent');
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
c.sprite(arch.x, G - 4, withFace(arch.st === 'back' && w ? AGENT.walk : AGENT.stand, blink(t, 5)), 'agent');
|
|
147
|
+
}
|
|
148
|
+
for (const p of puffs)
|
|
149
|
+
c.put(p.x, p.y, p.ch, p.tone);
|
|
150
|
+
},
|
|
151
|
+
status() {
|
|
152
|
+
return `archivadas ${archived} · basura ${bin}%`;
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion, prefer-const -- scene arrays are indexed within bounds; particle arrays are mutated in place. */
|
|
2
|
+
import { AGENT, blink, drawCloud, drawGround, rng, soilLine, withFace } from '../engine.js';
|
|
3
|
+
const W = 78;
|
|
4
|
+
const H = 12;
|
|
5
|
+
const G = 10;
|
|
6
|
+
const FRONT = 57;
|
|
7
|
+
const GAP = 7;
|
|
8
|
+
const CLOCK = 64;
|
|
9
|
+
const PUNCH = [' .-. ', '(o.o) ', '( )=]', " ' ' "];
|
|
10
|
+
const TAP = [' .-. ', '(o.o)', '( )', " ' ."];
|
|
11
|
+
const TALK = ['(zzz)', '(ya?)', '(café..)', '(...)', '(lunes)'];
|
|
12
|
+
export const clockIn = {
|
|
13
|
+
id: 'clockIn',
|
|
14
|
+
title: 'Ponchando entrada',
|
|
15
|
+
useFor: 'Inicio del día / cargando la app',
|
|
16
|
+
width: W,
|
|
17
|
+
height: H,
|
|
18
|
+
create(seed = 3) {
|
|
19
|
+
const r = rng(seed);
|
|
20
|
+
const soil = [soilLine(W, r)];
|
|
21
|
+
let t = 0;
|
|
22
|
+
let q = [];
|
|
23
|
+
let gone = [];
|
|
24
|
+
let nid = 6;
|
|
25
|
+
let punched = 0;
|
|
26
|
+
let flash = 0;
|
|
27
|
+
let mins = 7 * 60 + 52;
|
|
28
|
+
for (let i = 0; i < 5; i++)
|
|
29
|
+
q.push({ x: FRONT - GAP * i, k: 0, id: i + 1 });
|
|
30
|
+
return {
|
|
31
|
+
step() {
|
|
32
|
+
t++;
|
|
33
|
+
if (t % 25 === 0) {
|
|
34
|
+
mins++;
|
|
35
|
+
if (mins > 8 * 60 + 10)
|
|
36
|
+
mins = 7 * 60 + 52;
|
|
37
|
+
}
|
|
38
|
+
q.forEach((a, i) => {
|
|
39
|
+
const tx = FRONT - GAP * i;
|
|
40
|
+
a.moving = a.x < tx;
|
|
41
|
+
if (a.moving)
|
|
42
|
+
a.x = Math.min(tx, a.x + 0.5);
|
|
43
|
+
});
|
|
44
|
+
const f = q[0];
|
|
45
|
+
if (f && f.x === FRONT) {
|
|
46
|
+
f.k++;
|
|
47
|
+
if (f.k === 9) {
|
|
48
|
+
punched++;
|
|
49
|
+
flash = 10;
|
|
50
|
+
}
|
|
51
|
+
if (f.k > 16) {
|
|
52
|
+
gone.push(q.shift());
|
|
53
|
+
q.push({ x: -6, k: 0, id: nid++ });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const a of gone)
|
|
57
|
+
a.x += 0.5;
|
|
58
|
+
gone = gone.filter((a) => a.x < W);
|
|
59
|
+
if (flash)
|
|
60
|
+
flash--;
|
|
61
|
+
},
|
|
62
|
+
draw(c) {
|
|
63
|
+
drawCloud(c, t, 0.05, 50, 0);
|
|
64
|
+
drawGround(c, G, soil);
|
|
65
|
+
c.put(28, 1, ' ______________ ', 'wood');
|
|
66
|
+
c.put(28, 2, '| entrada 8:00 |', 'wood');
|
|
67
|
+
c.put(28, 3, "'--------------'", 'wood');
|
|
68
|
+
c.put(28, 2, '|', 'wood');
|
|
69
|
+
c.put(30, 2, 'entrada 8:00', 'text');
|
|
70
|
+
c.put(74, G - 6, '____', 'wood');
|
|
71
|
+
for (let y = G - 5; y < G; y++)
|
|
72
|
+
c.put(74, y, '| |', 'wood');
|
|
73
|
+
c.put(76, G - 3, 'o', 'spark');
|
|
74
|
+
const talker = Math.floor(t / 20) % 5;
|
|
75
|
+
q.forEach((a, i) => {
|
|
76
|
+
const w = Math.floor(t / 2) % 2;
|
|
77
|
+
let sp = a.moving ? (w ? AGENT.stand : AGENT.walk) : AGENT.stand;
|
|
78
|
+
if (i === 0 && a.k >= 4 && a.k <= 14)
|
|
79
|
+
sp = PUNCH;
|
|
80
|
+
if (!a.moving && a.id % 3 === 2 && i > 0)
|
|
81
|
+
sp = Math.floor(t / 3) % 2 ? AGENT.stand : TAP;
|
|
82
|
+
const face = i === 0 && a.k > 9 ? '^.^' : a.id % 3 === 0 ? blink(t, a.id, '-.o') : blink(t, a.id);
|
|
83
|
+
c.sprite(a.x, G - 4, withFace(sp, face), 'agent');
|
|
84
|
+
if (i === talker && i > 0 && !a.moving)
|
|
85
|
+
c.put(a.x - 1, G - 5, TALK[a.id % 5], 'cloud');
|
|
86
|
+
});
|
|
87
|
+
for (const a of gone)
|
|
88
|
+
c.sprite(a.x, G - 4, withFace(Math.floor(t / 2) % 2 ? AGENT.stand : AGENT.walk, '^.^'), 'agent');
|
|
89
|
+
c.put(CLOCK, G - 5, ' ______ ', 'rock');
|
|
90
|
+
c.put(CLOCK, G - 4, '| |', 'rock');
|
|
91
|
+
c.put(CLOCK, G - 3, '| |', 'rock');
|
|
92
|
+
c.put(CLOCK, G - 2, '|[____]|', 'rock');
|
|
93
|
+
c.put(CLOCK, G - 1, "'------'", 'rock');
|
|
94
|
+
const hh = String(Math.floor(mins / 60)).padStart(2, '0');
|
|
95
|
+
const mm = String(mins % 60).padStart(2, '0');
|
|
96
|
+
c.put(CLOCK + 1, G - 4, hh + (t % 10 < 5 ? ':' : ' ') + mm, 'accent');
|
|
97
|
+
if (flash)
|
|
98
|
+
c.put(CLOCK + 3, G - 3, 'ok', 'good');
|
|
99
|
+
},
|
|
100
|
+
status() {
|
|
101
|
+
return `ponchados ${punched} · en fila ${q.length}`;
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
};
|