@xynogen/pix-optimizer 1.1.6 → 1.1.8
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/README.md +40 -39
- package/package.json +1 -1
- package/src/caveman.ts +7 -0
- package/src/index.ts +2 -2
- package/src/json.ts +5 -0
- package/src/persist.test.ts +56 -0
- package/src/persist.ts +50 -0
- package/src/ponytail.ts +6 -0
- package/src/rtk.ts +5 -0
- package/src/status.ts +5 -5
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pix-optimizer
|
|
2
2
|
|
|
3
|
-
Token-optimization suite for Pi Coding Agent.
|
|
4
|
-
extension via `src/index.ts`, fronted by a single `/
|
|
3
|
+
Token-optimization suite for Pi Coding Agent. Four tools wired into one
|
|
4
|
+
extension via `src/index.ts`, fronted by a single `/optimizer` command and one
|
|
5
5
|
shared status-bar cell:
|
|
6
6
|
|
|
7
7
|
- **Caveman** (``) — terse-output system prompt
|
|
@@ -11,16 +11,18 @@ shared status-bar cell:
|
|
|
11
11
|
|
|
12
12
|
## Command
|
|
13
13
|
|
|
14
|
-
One command
|
|
14
|
+
One command opens an interactive overlay that fronts every tool:
|
|
15
15
|
|
|
16
16
|
```text
|
|
17
|
-
/
|
|
18
|
-
/opt caveman <level> → set caveman level (1/2/3/lite/full/ultra/micro/off/config)
|
|
19
|
-
/opt rtk [on|off] → toggle RTK rewriting
|
|
20
|
-
/opt toon [on|off] → toggle jq+TOON guidance
|
|
21
|
-
/opt ponytail <level> → set ponytail level (1/2/3/lite/full/ultra/off/config)
|
|
17
|
+
/optimizer → open the overlay (←→ cycle value, ↑↓ move, esc close)
|
|
22
18
|
```
|
|
23
19
|
|
|
20
|
+
There is no text-arg form: the overlay is the only UI. Selecting a value calls
|
|
21
|
+
the tool's `run()` handler, which persists the new value and repaints the
|
|
22
|
+
shared status cell. Headless/test fallbacks print a plain status summary.
|
|
23
|
+
|
|
24
|
+
State persists to `~/.pi/agent/optimizer.json` (caveman/rtk/toon/ponytail).
|
|
25
|
+
|
|
24
26
|
## Status bar
|
|
25
27
|
|
|
26
28
|
A single cell always shows the enabled icons in a fixed order (` `), color-
|
|
@@ -32,15 +34,15 @@ coded by state: **accent** when the tool is enabled, **dim** when disabled.
|
|
|
32
34
|
|
|
33
35
|
Cuts ~75% of output tokens while keeping full technical accuracy.
|
|
34
36
|
|
|
35
|
-
|
|
|
36
|
-
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
37
|
+
| Level | Description |
|
|
38
|
+
|-------|------------------------------|
|
|
39
|
+
| lite | Professional, no fluff |
|
|
40
|
+
| full | Classic caveman |
|
|
41
|
+
| ultra | Maximum compression |
|
|
42
|
+
| micro | Experimental prompt-minimized |
|
|
41
43
|
|
|
42
|
-
`/
|
|
43
|
-
|
|
44
|
+
The `/optimizer` overlay opens a settings dialog when needed. Default level
|
|
45
|
+
for new sessions is restored from `~/.pi/agent/optimizer.json`.
|
|
44
46
|
|
|
45
47
|
### RTK Tool Rewriting (``)
|
|
46
48
|
|
|
@@ -88,14 +90,11 @@ writing code the agent stops at the first rung that holds: does this need to
|
|
|
88
90
|
exist → stdlib → native platform → installed dep → one line → minimum that
|
|
89
91
|
works. Validation, error handling, security, and accessibility are never cut.
|
|
90
92
|
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
|
|
97
|
-
`/opt ponytail config` opens a settings dialog. Default level for new sessions
|
|
98
|
-
and status-bar visibility are saved to `~/.pi/agent/ponytail.json`.
|
|
93
|
+
| Level | Description |
|
|
94
|
+
|-------|--------------------------------------|
|
|
95
|
+
| lite | Name the lazier alternative, you pick |
|
|
96
|
+
| full | The ladder enforced (default) |
|
|
97
|
+
| ultra | YAGNI extremist |
|
|
99
98
|
|
|
100
99
|
**No install required** — pure prompt injection, no external binary or PATH
|
|
101
100
|
dependency (unlike RTK and TOON).
|
|
@@ -116,16 +115,18 @@ pi install npm:@xynogen/pix-optimizer
|
|
|
116
115
|
|
|
117
116
|
| File | Role |
|
|
118
117
|
|-------------------|-----------------------------------------------------------|
|
|
119
|
-
| `src/index.ts` | Wires the four tools + shared status, registers `/
|
|
120
|
-
| `src/opt.ts` | The `/
|
|
118
|
+
| `src/index.ts` | Wires the four tools + shared status, registers `/optimizer` |
|
|
119
|
+
| `src/opt.ts` | The `/optimizer` overlay UI (keyboard nav + cycling) |
|
|
121
120
|
| `src/status.ts` | Shared status-bar cell + `OptimizerHandle` contract |
|
|
122
|
-
| `src/caveman.ts` | Caveman logic, levels, prompt
|
|
121
|
+
| `src/caveman.ts` | Caveman logic, levels, prompt |
|
|
123
122
|
| `src/rtk.ts` | RTK prompt + bash command rewriting |
|
|
124
123
|
| `src/json.ts` | jq+TOON guidance, heuristics, system-prompt injection |
|
|
125
|
-
| `src/ponytail.ts` | Ponytail logic, levels, prompt
|
|
124
|
+
| `src/ponytail.ts` | Ponytail logic, levels, prompt |
|
|
125
|
+
| `src/persist.ts` | Disk-backed `~/.pi/agent/optimizer.json` persistence |
|
|
126
|
+
| `src/tool-result-filter.ts` | Strips model-guidance warnings from tool_result |
|
|
126
127
|
|
|
127
128
|
Each tool registers its own lifecycle hooks and exposes an `OptimizerHandle`
|
|
128
|
-
that `/
|
|
129
|
+
that `/optimizer` dispatches to. All four share one `OptimizerStatus`.
|
|
129
130
|
|
|
130
131
|
## Development
|
|
131
132
|
|
|
@@ -137,22 +138,22 @@ bun test
|
|
|
137
138
|
|
|
138
139
|
This package was built by merging two upstream Pi community packages:
|
|
139
140
|
|
|
140
|
-
- **Caveman mode** — merged from
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
- **Caveman mode** — merged from `npm:pi-caveman`. Reimplemented here with
|
|
142
|
+
multiple compression levels and integration with the shared `/optimizer`
|
|
143
|
+
command.
|
|
143
144
|
|
|
144
|
-
- **RTK rewriting** — merged from `npm:pi-rtk-optimizer`. Reimplemented here
|
|
145
|
-
approach: prompt injection + live bash command rewriting
|
|
146
|
-
(`&&`, `||`, `;`, `|`).
|
|
145
|
+
- **RTK rewriting** — merged from `npm:pi-rtk-optimizer`. Reimplemented here
|
|
146
|
+
with a two-layer approach: prompt injection + live bash command rewriting
|
|
147
|
+
that handles chained commands (`&&`, `||`, `;`, `|`).
|
|
147
148
|
|
|
148
149
|
- **Ponytail mode** — ruleset adapted from [`git:github.com/DietrichGebert/ponytail`](https://github.com/DietrichGebert/ponytail),
|
|
149
|
-
the "lazy senior dev" skill. Reimplemented here as a native `/
|
|
150
|
-
|
|
150
|
+
the "lazy senior dev" skill. Reimplemented here as a native `/optimizer`
|
|
151
|
+
tool with three intensity levels — no external hooks or files. The
|
|
151
152
|
ruleset (the YAGNI ladder + safety carve-outs) is rewritten as a system-prompt fragment.
|
|
152
153
|
|
|
153
154
|
All upstreams are MIT licensed. No codebase was copied directly — the logic was
|
|
154
|
-
rewritten and combined into a single extension with a unified `/
|
|
155
|
-
This package does not sync back to any upstream.
|
|
155
|
+
rewritten and combined into a single extension with a unified `/optimizer`
|
|
156
|
+
command and shared status bar. This package does not sync back to any upstream.
|
|
156
157
|
|
|
157
158
|
## Full distro
|
|
158
159
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-optimizer",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "Performance optimization suite for Pi Coding Agent - caveman mode + RTK tool rewriting + jq/TOON JSON compression + ponytail lazy-dev mode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/caveman.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
ExtensionCommandContext,
|
|
11
11
|
ExtensionContext,
|
|
12
12
|
} from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { loadOptValue, saveOptValue } from "./persist.ts";
|
|
13
14
|
import type { OptimizerHandle, OptimizerStatus } from "./status.ts";
|
|
14
15
|
|
|
15
16
|
// ── Levels ────────────────────────────────────────────────────────────────────
|
|
@@ -155,11 +156,16 @@ export function caveman(
|
|
|
155
156
|
// -- Restore live level from the session log on load --
|
|
156
157
|
|
|
157
158
|
pi.on("session_start", async (_event, ctx) => {
|
|
159
|
+
// Session log first (survives in-session branch nav), then disk (survives
|
|
160
|
+
// a full quit/restart). Disk wins when present so a new session restores
|
|
161
|
+
// the last chosen level instead of defaulting to off.
|
|
158
162
|
for (const entry of ctx.sessionManager.getEntries()) {
|
|
159
163
|
if (entry.type === "custom" && entry.customType === "caveman-level") {
|
|
160
164
|
level = (entry.data as { level: Level })?.level ?? level;
|
|
161
165
|
}
|
|
162
166
|
}
|
|
167
|
+
const saved = loadOptValue("caveman");
|
|
168
|
+
if (saved && LEVELS.includes(saved as Level)) level = saved as Level;
|
|
163
169
|
syncStatus(ctx);
|
|
164
170
|
});
|
|
165
171
|
|
|
@@ -182,6 +188,7 @@ export function caveman(
|
|
|
182
188
|
level = resolved;
|
|
183
189
|
|
|
184
190
|
pi.appendEntry("caveman-level", { level });
|
|
191
|
+
saveOptValue("caveman", level);
|
|
185
192
|
syncStatus(ctx);
|
|
186
193
|
|
|
187
194
|
ctx.ui.notify(
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pix-optimizer — token-optimization suite for Pi Coding Agent.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Four tools, combined into one extension + one command:
|
|
5
5
|
* - caveman: terse-output system prompt
|
|
6
6
|
* - rtk: prefixes shell commands with `rtk` + injects RTK prompt
|
|
7
7
|
* - toon: jq + TOON guidance for dense JSON (+ bundled skill)
|
|
8
8
|
* - ponytail: lazy-senior-dev system prompt (minimal code, YAGNI)
|
|
9
9
|
*
|
|
10
|
-
* They share ONE status-bar cell ( ,
|
|
10
|
+
* They share ONE status-bar cell ( , all four always shown — dimmed when off, accented when on) and ONE
|
|
11
11
|
* command (/optimizer — an interactive overlay). index.ts wires lifecycle hooks
|
|
12
12
|
* via each module, then registers the overlay command from their handles.
|
|
13
13
|
*/
|
package/src/json.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
ExtensionCommandContext,
|
|
23
23
|
ExtensionContext,
|
|
24
24
|
} from "@earendil-works/pi-coding-agent";
|
|
25
|
+
import { loadOptValue, saveOptValue } from "./persist.ts";
|
|
25
26
|
import type { OptimizerHandle, OptimizerStatus } from "./status.ts";
|
|
26
27
|
|
|
27
28
|
// ── System prompt ───────────────────────────────────────────────────────────
|
|
@@ -225,6 +226,9 @@ export function json(
|
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
pi.on("session_start", async (_event, ctx) => {
|
|
229
|
+
// Restore the user's on/off choice from disk (survives quit/restart).
|
|
230
|
+
const saved = loadOptValue("toon");
|
|
231
|
+
if (saved === "on" || saved === "off") enabled = saved === "on";
|
|
228
232
|
syncStatus(ctx);
|
|
229
233
|
});
|
|
230
234
|
pi.on("agent_start", async (_event, ctx) => {
|
|
@@ -277,6 +281,7 @@ export function json(
|
|
|
277
281
|
ctx: ExtensionCommandContext,
|
|
278
282
|
): Promise<void> {
|
|
279
283
|
enabled = value === "on";
|
|
284
|
+
saveOptValue("toon", enabled ? "on" : "off");
|
|
280
285
|
|
|
281
286
|
syncStatus(ctx);
|
|
282
287
|
ctx.ui.notify(`JSON/TOON guidance ${enabled ? "on" : "off"}.`, "info");
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for the cross-session reset bug: optimizer tool states
|
|
3
|
+
* (caveman/ponytail/rtk/toon) were lost on a full quit/restart because they
|
|
4
|
+
* only persisted to the session log. persist.ts adds disk persistence so a
|
|
5
|
+
* value written in one session is readable in the next.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
9
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { loadOptValue, saveOptValue } from "./persist.ts";
|
|
13
|
+
|
|
14
|
+
let tmpAgentDir: string;
|
|
15
|
+
|
|
16
|
+
beforeAll(() => {
|
|
17
|
+
tmpAgentDir = mkdtempSync(join(tmpdir(), "optimizer-persist-test-"));
|
|
18
|
+
process.env.PI_CODING_AGENT_DIR = tmpAgentDir;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterAll(() => {
|
|
22
|
+
delete process.env.PI_CODING_AGENT_DIR;
|
|
23
|
+
try {
|
|
24
|
+
rmSync(tmpAgentDir, { recursive: true });
|
|
25
|
+
} catch {
|
|
26
|
+
// temp dir may already be gone — safe to ignore
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("optimizer persistence", () => {
|
|
31
|
+
test("returns undefined before anything is saved", () => {
|
|
32
|
+
expect(loadOptValue("caveman")).toBeUndefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("round-trips a single tool value across save/load (new-session sim)", () => {
|
|
36
|
+
saveOptValue("caveman", "lite");
|
|
37
|
+
// A fresh load (as a new session would do) sees the persisted value.
|
|
38
|
+
expect(loadOptValue("caveman")).toBe("lite");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("persists each tool independently in one shared file", () => {
|
|
42
|
+
saveOptValue("ponytail", "full");
|
|
43
|
+
saveOptValue("rtk", "off");
|
|
44
|
+
saveOptValue("toon", "on");
|
|
45
|
+
expect(loadOptValue("caveman")).toBe("lite");
|
|
46
|
+
expect(loadOptValue("ponytail")).toBe("full");
|
|
47
|
+
expect(loadOptValue("rtk")).toBe("off");
|
|
48
|
+
expect(loadOptValue("toon")).toBe("on");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("overwriting one tool leaves the others intact", () => {
|
|
52
|
+
saveOptValue("caveman", "ultra");
|
|
53
|
+
expect(loadOptValue("caveman")).toBe("ultra");
|
|
54
|
+
expect(loadOptValue("ponytail")).toBe("full");
|
|
55
|
+
});
|
|
56
|
+
});
|
package/src/persist.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* persist.ts — disk-backed persistence for the /optimizer tool states.
|
|
3
|
+
*
|
|
4
|
+
* caveman/ponytail previously saved only to the session log (lost on a fresh
|
|
5
|
+
* session); rtk/toon never persisted at all. This stores every tool's current
|
|
6
|
+
* value in one file under the agent dir so the picker survives a full quit and
|
|
7
|
+
* restart. Each tool reads its value on session_start and writes on run().
|
|
8
|
+
*
|
|
9
|
+
* ~/.pi/agent/optimizer.json → { "caveman": "lite", "rtk": "on", ... }
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import type { OptimizerTool } from "./status.ts";
|
|
16
|
+
|
|
17
|
+
type OptimizerConfig = Partial<Record<OptimizerTool, string>>;
|
|
18
|
+
|
|
19
|
+
function getStatePath(): string {
|
|
20
|
+
return join(getAgentDir(), "optimizer.json");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readAll(): OptimizerConfig {
|
|
24
|
+
try {
|
|
25
|
+
const sp = getStatePath();
|
|
26
|
+
if (!existsSync(sp)) return {};
|
|
27
|
+
const raw = JSON.parse(readFileSync(sp, "utf-8")) as OptimizerConfig;
|
|
28
|
+
return raw && typeof raw === "object" ? raw : {};
|
|
29
|
+
} catch {
|
|
30
|
+
// Missing, corrupt, or no getAgentDir (tests/headless) — start clean.
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Read a single tool's persisted value, or undefined if none. */
|
|
36
|
+
export function loadOptValue(tool: OptimizerTool): string | undefined {
|
|
37
|
+
return readAll()[tool];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Persist a single tool's value, merging into the shared config file. */
|
|
41
|
+
export function saveOptValue(tool: OptimizerTool, value: string): void {
|
|
42
|
+
try {
|
|
43
|
+
const sp = getStatePath();
|
|
44
|
+
mkdirSync(dirname(sp), { recursive: true });
|
|
45
|
+
const next = { ...readAll(), [tool]: value };
|
|
46
|
+
writeFileSync(sp, JSON.stringify(next, null, 2), "utf-8");
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.warn(`optimizer: persist ${tool} failed:`, err);
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/ponytail.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
ExtensionCommandContext,
|
|
15
15
|
ExtensionContext,
|
|
16
16
|
} from "@earendil-works/pi-coding-agent";
|
|
17
|
+
import { loadOptValue, saveOptValue } from "./persist.ts";
|
|
17
18
|
import type { OptimizerHandle, OptimizerStatus } from "./status.ts";
|
|
18
19
|
|
|
19
20
|
// ── Levels ────────────────────────────────────────────────────────────────────
|
|
@@ -159,11 +160,15 @@ export function ponytail(
|
|
|
159
160
|
// -- Restore live level from the session log on load --
|
|
160
161
|
|
|
161
162
|
pi.on("session_start", async (_event, ctx) => {
|
|
163
|
+
// Session log first (in-session branch nav), then disk (survives a full
|
|
164
|
+
// quit/restart). Disk wins when present.
|
|
162
165
|
for (const entry of ctx.sessionManager.getEntries()) {
|
|
163
166
|
if (entry.type === "custom" && entry.customType === "ponytail-level") {
|
|
164
167
|
level = (entry.data as { level: Level })?.level ?? level;
|
|
165
168
|
}
|
|
166
169
|
}
|
|
170
|
+
const saved = loadOptValue("ponytail");
|
|
171
|
+
if (saved && LEVELS.includes(saved as Level)) level = saved as Level;
|
|
167
172
|
syncStatus(ctx);
|
|
168
173
|
});
|
|
169
174
|
|
|
@@ -186,6 +191,7 @@ export function ponytail(
|
|
|
186
191
|
level = resolved;
|
|
187
192
|
|
|
188
193
|
pi.appendEntry("ponytail-level", { level });
|
|
194
|
+
saveOptValue("ponytail", level);
|
|
189
195
|
syncStatus(ctx);
|
|
190
196
|
|
|
191
197
|
ctx.ui.notify(
|
package/src/rtk.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
ExtensionCommandContext,
|
|
12
12
|
ExtensionContext,
|
|
13
13
|
} from "@earendil-works/pi-coding-agent";
|
|
14
|
+
import { loadOptValue, saveOptValue } from "./persist.ts";
|
|
14
15
|
import type { OptimizerHandle, OptimizerStatus } from "./status.ts";
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -283,6 +284,9 @@ export function rtk(
|
|
|
283
284
|
// Keep the status indicator in sync across the agent lifecycle. Probe
|
|
284
285
|
// availability on session start so the icon reflects reality immediately.
|
|
285
286
|
pi.on("session_start", async (_event, ctx) => {
|
|
287
|
+
// Restore the user's on/off choice from disk (survives quit/restart).
|
|
288
|
+
const saved = loadOptValue("rtk");
|
|
289
|
+
if (saved === "on" || saved === "off") enabled = saved === "on";
|
|
286
290
|
const probe = await checkRtkAvailability();
|
|
287
291
|
if (!probe.available && !warnedMissing) {
|
|
288
292
|
ctx.ui.notify(
|
|
@@ -307,6 +311,7 @@ export function rtk(
|
|
|
307
311
|
ctx: ExtensionCommandContext,
|
|
308
312
|
): Promise<void> {
|
|
309
313
|
enabled = value === "on";
|
|
314
|
+
saveOptValue("rtk", enabled ? "on" : "off");
|
|
310
315
|
|
|
311
316
|
await checkRtkAvailability();
|
|
312
317
|
syncStatus(ctx);
|
package/src/status.ts
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* caveman / rtk / toon / ponytail each toggle independently, but they're all the same
|
|
5
5
|
* class of thing (token-optimization tools), so they share ONE status cell
|
|
6
6
|
* instead of three. Each tool reports its on/off state into a single registry;
|
|
7
|
-
* the cell renders
|
|
8
|
-
*
|
|
7
|
+
* the cell renders ALL four icons in a fixed order — accent-colored when the
|
|
8
|
+
* tool is enabled, dim when disabled. The cell is never empty.
|
|
9
9
|
*
|
|
10
|
-
* all on:
|
|
11
|
-
* caveman off:
|
|
12
|
-
* all off:
|
|
10
|
+
* all on: all four accent
|
|
11
|
+
* caveman off: caveman dim, rest accent
|
|
12
|
+
* all off: all four dim
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type {
|