@litlab/audx 0.0.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.
- package/README.md +86 -0
- package/dist/codegen/theme-codegen.d.ts +12 -0
- package/dist/codegen/theme-codegen.d.ts.map +1 -0
- package/dist/codegen/theme-codegen.js +153 -0
- package/dist/codegen/theme-codegen.js.map +1 -0
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +120 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/diff.d.ts +2 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +103 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/generate.d.ts +12 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +96 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +79 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +14 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +93 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/remove.d.ts +2 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +71 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/theme.d.ts +31 -0
- package/dist/commands/theme.d.ts.map +1 -0
- package/dist/commands/theme.js +142 -0
- package/dist/commands/theme.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +123 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/core/alias-resolver.d.ts +24 -0
- package/dist/core/alias-resolver.d.ts.map +1 -0
- package/dist/core/alias-resolver.js +87 -0
- package/dist/core/alias-resolver.js.map +1 -0
- package/dist/core/config.d.ts +21 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +43 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/file-writer.d.ts +14 -0
- package/dist/core/file-writer.d.ts.map +1 -0
- package/dist/core/file-writer.js +90 -0
- package/dist/core/file-writer.js.map +1 -0
- package/dist/core/package-manager.d.ts +3 -0
- package/dist/core/package-manager.d.ts.map +1 -0
- package/dist/core/package-manager.js +17 -0
- package/dist/core/package-manager.js.map +1 -0
- package/dist/core/registry.d.ts +18 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +69 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/theme-manager.d.ts +35 -0
- package/dist/core/theme-manager.d.ts.map +1 -0
- package/dist/core/theme-manager.js +94 -0
- package/dist/core/theme-manager.js.map +1 -0
- package/dist/core/utils.d.ts +22 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +44 -0
- package/dist/core/utils.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +43 -0
- package/dist/types.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# audx
|
|
2
|
+
|
|
3
|
+
CLI tool for distributing and managing UI sounds from the [audx](https://audx.dev) registry.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g audx
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use directly with `npx`:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx audx <command>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Initialize audx in your project
|
|
21
|
+
audx init
|
|
22
|
+
|
|
23
|
+
# Browse available sounds
|
|
24
|
+
audx list
|
|
25
|
+
|
|
26
|
+
# Add a sound
|
|
27
|
+
audx add click-001
|
|
28
|
+
|
|
29
|
+
# Set up themes
|
|
30
|
+
audx theme init
|
|
31
|
+
audx theme map click click-001
|
|
32
|
+
audx theme generate
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Commands
|
|
36
|
+
|
|
37
|
+
| Command | Description |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `audx init` | Initialize audx config in your project |
|
|
40
|
+
| `audx add <sounds...>` | Add sounds from the registry |
|
|
41
|
+
| `audx remove <sounds...>` | Remove installed sounds |
|
|
42
|
+
| `audx list` | List available sounds (`--tag`, `--search`) |
|
|
43
|
+
| `audx diff` | Check for updates to installed sounds |
|
|
44
|
+
| `audx update [sound]` | Update installed sounds from registry |
|
|
45
|
+
| `audx generate "<prompt>"` | Generate a sound from a text prompt (`--name`, `--duration`) |
|
|
46
|
+
| `audx theme init` | Create theme configuration |
|
|
47
|
+
| `audx theme set <name>` | Switch active theme |
|
|
48
|
+
| `audx theme map <semantic> <sound>` | Map a semantic name to a sound |
|
|
49
|
+
| `audx theme create <name>` | Create a new theme |
|
|
50
|
+
| `audx theme list` | List all themes |
|
|
51
|
+
| `audx theme generate` | Generate `sound-theme.ts` |
|
|
52
|
+
|
|
53
|
+
## How It Works
|
|
54
|
+
|
|
55
|
+
audx manages UI sounds as TypeScript modules with base64-encoded audio data — no external files to serve. Sounds are fetched from the audx registry and written directly into your project.
|
|
56
|
+
|
|
57
|
+
The theme system lets you reference sounds by semantic purpose (`play("success")`) instead of file names, and swap entire sound palettes by switching themes.
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
Running `audx init` creates `audx.config.json`:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"soundDir": "src/sounds",
|
|
66
|
+
"libDir": "src/lib",
|
|
67
|
+
"registryUrl": "https://audx.dev",
|
|
68
|
+
"packageManager": "pnpm",
|
|
69
|
+
"aliases": {
|
|
70
|
+
"lib": "@/lib",
|
|
71
|
+
"hooks": "@/hooks",
|
|
72
|
+
"sounds": "@/sounds"
|
|
73
|
+
},
|
|
74
|
+
"installedSounds": {}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Path aliases are auto-detected from your `tsconfig.json`.
|
|
79
|
+
|
|
80
|
+
## Requirements
|
|
81
|
+
|
|
82
|
+
- Node.js 18+
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AliasMap, AudxConfig, ThemeConfig } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate the `sound-theme.ts` TypeScript source from a theme configuration.
|
|
4
|
+
*
|
|
5
|
+
* The generated file exports:
|
|
6
|
+
* - `SemanticSoundName` — union type of all semantic names present in the themes
|
|
7
|
+
* - `soundThemes` — object mapping theme names to their sound mappings
|
|
8
|
+
* - `play(name)` — plays the mapped sound in the active theme (no-ops for null)
|
|
9
|
+
* - `setSoundTheme(themeName)` — switches the active theme at runtime
|
|
10
|
+
*/
|
|
11
|
+
export declare function generate(themeConfig: ThemeConfig, aliasMap: AliasMap, config: AudxConfig): string;
|
|
12
|
+
//# sourceMappingURL=theme-codegen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-codegen.d.ts","sourceRoot":"","sources":["../../src/codegen/theme-codegen.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAErE;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACvB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,UAAU,GAChB,MAAM,CAuER"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { resolveImport } from "../core/alias-resolver.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate the `sound-theme.ts` TypeScript source from a theme configuration.
|
|
4
|
+
*
|
|
5
|
+
* The generated file exports:
|
|
6
|
+
* - `SemanticSoundName` — union type of all semantic names present in the themes
|
|
7
|
+
* - `soundThemes` — object mapping theme names to their sound mappings
|
|
8
|
+
* - `play(name)` — plays the mapped sound in the active theme (no-ops for null)
|
|
9
|
+
* - `setSoundTheme(themeName)` — switches the active theme at runtime
|
|
10
|
+
*/
|
|
11
|
+
export function generate(themeConfig, aliasMap, config) {
|
|
12
|
+
// The generated file lives in libDir (e.g. "src/lib/sound-theme.ts")
|
|
13
|
+
const generatedFilePath = `${config.libDir}/sound-theme.ts`;
|
|
14
|
+
// Collect all unique semantic names across all themes
|
|
15
|
+
const semanticNames = collectSemanticNames(themeConfig);
|
|
16
|
+
// Collect all unique non-null sound file paths across all themes
|
|
17
|
+
const soundPaths = collectSoundPaths(themeConfig);
|
|
18
|
+
// Build import statements for each sound module
|
|
19
|
+
const imports = buildImports(soundPaths, generatedFilePath, aliasMap);
|
|
20
|
+
// Resolve the audio-engine import path
|
|
21
|
+
const audioEngineImport = resolveImport(aliasMap, generatedFilePath, `${config.libDir}/audio-engine.ts`);
|
|
22
|
+
const lines = [];
|
|
23
|
+
// File header
|
|
24
|
+
lines.push("// This file is auto-generated by audx. Do not edit manually.");
|
|
25
|
+
lines.push("");
|
|
26
|
+
// Audio engine import
|
|
27
|
+
lines.push(`import { playAudio } from "${audioEngineImport}";`);
|
|
28
|
+
// Sound module imports
|
|
29
|
+
for (const { importName, importPath } of imports) {
|
|
30
|
+
lines.push(`import { default as ${importName} } from "${importPath}";`);
|
|
31
|
+
}
|
|
32
|
+
lines.push("");
|
|
33
|
+
// SemanticSoundName type union
|
|
34
|
+
lines.push(buildSemanticSoundNameType(semanticNames));
|
|
35
|
+
lines.push("");
|
|
36
|
+
// soundThemes object
|
|
37
|
+
lines.push(buildSoundThemesObject(themeConfig, soundPaths, generatedFilePath, aliasMap));
|
|
38
|
+
lines.push("");
|
|
39
|
+
// Runtime state
|
|
40
|
+
lines.push(`let activeTheme = "${themeConfig.activeTheme}";`);
|
|
41
|
+
lines.push("");
|
|
42
|
+
// setSoundTheme function
|
|
43
|
+
lines.push("export function setSoundTheme(themeName: string): void {");
|
|
44
|
+
lines.push(" activeTheme = themeName;");
|
|
45
|
+
lines.push("}");
|
|
46
|
+
lines.push("");
|
|
47
|
+
// play function
|
|
48
|
+
lines.push("export function play(name: SemanticSoundName): void {");
|
|
49
|
+
lines.push(" const theme = soundThemes[activeTheme];");
|
|
50
|
+
lines.push(" if (!theme) return;");
|
|
51
|
+
lines.push(" const asset = theme[name];");
|
|
52
|
+
lines.push(" if (!asset) return;");
|
|
53
|
+
lines.push(" playAudio(asset.dataUri);");
|
|
54
|
+
lines.push("}");
|
|
55
|
+
lines.push("");
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Collect all unique semantic names across all themes.
|
|
60
|
+
*/
|
|
61
|
+
function collectSemanticNames(themeConfig) {
|
|
62
|
+
const names = new Set();
|
|
63
|
+
for (const mappings of Object.values(themeConfig.themes)) {
|
|
64
|
+
for (const key of Object.keys(mappings)) {
|
|
65
|
+
names.add(key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return Array.from(names).sort();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Collect all unique non-null sound file paths across all themes.
|
|
72
|
+
*/
|
|
73
|
+
function collectSoundPaths(themeConfig) {
|
|
74
|
+
const paths = new Set();
|
|
75
|
+
for (const mappings of Object.values(themeConfig.themes)) {
|
|
76
|
+
for (const value of Object.values(mappings)) {
|
|
77
|
+
if (value !== null) {
|
|
78
|
+
paths.add(value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return Array.from(paths).sort();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Derive a safe import variable name from a sound file path.
|
|
86
|
+
* e.g. "src/sounds/click-001.ts" → "click001Sound"
|
|
87
|
+
*/
|
|
88
|
+
function deriveImportName(soundPath) {
|
|
89
|
+
const fileName = soundPath.split("/").pop() ?? soundPath;
|
|
90
|
+
const baseName = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
91
|
+
// Convert kebab-case to camelCase and append "Sound"
|
|
92
|
+
const camel = baseName
|
|
93
|
+
.split("-")
|
|
94
|
+
.map((part, i) => i === 0
|
|
95
|
+
? part.toLowerCase()
|
|
96
|
+
: part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
97
|
+
.join("");
|
|
98
|
+
return `${camel}Sound`;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Build import statements for all sound modules.
|
|
102
|
+
*/
|
|
103
|
+
function buildImports(soundPaths, generatedFilePath, aliasMap) {
|
|
104
|
+
return soundPaths.map((soundPath) => {
|
|
105
|
+
const importPath = resolveImport(aliasMap, generatedFilePath, soundPath);
|
|
106
|
+
const importName = deriveImportName(soundPath);
|
|
107
|
+
return { importName, importPath, originalPath: soundPath };
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Build the `SemanticSoundName` type union string.
|
|
112
|
+
*/
|
|
113
|
+
function buildSemanticSoundNameType(semanticNames) {
|
|
114
|
+
if (semanticNames.length === 0) {
|
|
115
|
+
return "export type SemanticSoundName = never;";
|
|
116
|
+
}
|
|
117
|
+
const members = semanticNames.map((n) => `"${n}"`).join(" | ");
|
|
118
|
+
return `export type SemanticSoundName = ${members};`;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Build the `soundThemes` object source code.
|
|
122
|
+
*/
|
|
123
|
+
function buildSoundThemesObject(themeConfig, soundPaths, generatedFilePath, aliasMap) {
|
|
124
|
+
// Build a lookup from original path → import variable name
|
|
125
|
+
const pathToImportName = new Map();
|
|
126
|
+
for (const soundPath of soundPaths) {
|
|
127
|
+
pathToImportName.set(soundPath, deriveImportName(soundPath));
|
|
128
|
+
}
|
|
129
|
+
const lines = [];
|
|
130
|
+
lines.push("export const soundThemes: Record<string, Record<SemanticSoundName, { dataUri: string } | null>> = {");
|
|
131
|
+
for (const [themeName, mappings] of Object.entries(themeConfig.themes)) {
|
|
132
|
+
lines.push(` "${themeName}": {`);
|
|
133
|
+
const sortedEntries = Object.entries(mappings).sort(([a], [b]) => a.localeCompare(b));
|
|
134
|
+
for (const [semanticName, soundPath] of sortedEntries) {
|
|
135
|
+
if (soundPath === null) {
|
|
136
|
+
lines.push(` "${semanticName}": null,`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const importName = pathToImportName.get(soundPath);
|
|
140
|
+
if (importName) {
|
|
141
|
+
lines.push(` "${semanticName}": ${importName},`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
lines.push(` "${semanticName}": null,`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
lines.push(" },");
|
|
149
|
+
}
|
|
150
|
+
lines.push("};");
|
|
151
|
+
return lines.join("\n");
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=theme-codegen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-codegen.js","sourceRoot":"","sources":["../../src/codegen/theme-codegen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAiEA,wBAAsB,UAAU,CAC/B,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAwEf"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
import { loadFromTsConfig } from "../core/alias-resolver.js";
|
|
5
|
+
import * as ConfigManager from "../core/config.js";
|
|
6
|
+
import { writeRegistryFile } from "../core/file-writer.js";
|
|
7
|
+
import { fetchItem } from "../core/registry.js";
|
|
8
|
+
/**
|
|
9
|
+
* Prompt the user with a yes/no question via stdin/stdout.
|
|
10
|
+
* Returns `true` when the user answers y/yes (case-insensitive).
|
|
11
|
+
*/
|
|
12
|
+
function confirm(question) {
|
|
13
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
rl.question(question, (answer) => {
|
|
16
|
+
rl.close();
|
|
17
|
+
resolve(/^y(es)?$/i.test(answer.trim()));
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check whether a registry file would conflict with an existing file on disk.
|
|
23
|
+
* Returns the resolved target path if a conflict exists, otherwise null.
|
|
24
|
+
*/
|
|
25
|
+
function getConflictPath(file, targetDir, config) {
|
|
26
|
+
const fileName = basename(file.path);
|
|
27
|
+
let targetPath;
|
|
28
|
+
if (file.type === "registry:hook") {
|
|
29
|
+
const hooksDir = join(dirname(config.libDir), "hooks");
|
|
30
|
+
targetPath = join(hooksDir, fileName);
|
|
31
|
+
}
|
|
32
|
+
else if (file.type === "registry:lib") {
|
|
33
|
+
const normalised = file.path.replace(/\\/g, "/");
|
|
34
|
+
if (normalised.includes("/sounds/")) {
|
|
35
|
+
targetPath = join(targetDir, fileName);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
targetPath = join(config.libDir, fileName);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
targetPath = join(targetDir, fileName);
|
|
43
|
+
}
|
|
44
|
+
return existsSync(targetPath) ? targetPath : null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Determine whether a registry file is a shared dependency (lib or hook)
|
|
48
|
+
* rather than the primary sound module.
|
|
49
|
+
*/
|
|
50
|
+
function isDependencyFile(file) {
|
|
51
|
+
if (file.type === "registry:hook")
|
|
52
|
+
return true;
|
|
53
|
+
if (file.type === "registry:lib") {
|
|
54
|
+
const normalised = file.path.replace(/\\/g, "/");
|
|
55
|
+
return !normalised.includes("/sounds/");
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
export async function addCommand(sounds, projectRoot) {
|
|
60
|
+
// Requirement 3.8 — config must exist
|
|
61
|
+
if (!ConfigManager.exists(projectRoot)) {
|
|
62
|
+
console.error("Configuration not found. Run 'audx init' first.");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
const config = ConfigManager.read(projectRoot);
|
|
66
|
+
const aliasMap = loadFromTsConfig(projectRoot);
|
|
67
|
+
const updatedConfig = {
|
|
68
|
+
...config,
|
|
69
|
+
installedSounds: { ...config.installedSounds },
|
|
70
|
+
};
|
|
71
|
+
for (const soundName of sounds) {
|
|
72
|
+
try {
|
|
73
|
+
// Requirement 3.1 — fetch registry item
|
|
74
|
+
const item = await fetchItem(config.registryUrl, soundName);
|
|
75
|
+
const targetDir = join(projectRoot, config.soundDir);
|
|
76
|
+
const writtenFiles = [];
|
|
77
|
+
for (const file of item.files) {
|
|
78
|
+
// Requirement 3.5 — skip dependency files that already exist
|
|
79
|
+
if (isDependencyFile(file)) {
|
|
80
|
+
const conflictPath = getConflictPath(file, targetDir, config);
|
|
81
|
+
if (conflictPath) {
|
|
82
|
+
writtenFiles.push(conflictPath);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Requirement 3.9 — prompt on file conflicts for non-dependency files
|
|
88
|
+
const conflictPath = getConflictPath(file, targetDir, config);
|
|
89
|
+
if (conflictPath) {
|
|
90
|
+
const overwrite = await confirm(`File '${conflictPath}' already exists. Overwrite? (y/N) `);
|
|
91
|
+
if (!overwrite) {
|
|
92
|
+
console.log(` Skipped ${conflictPath}`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Requirement 3.2, 3.3 — write file with import rewriting
|
|
98
|
+
const writtenPath = writeRegistryFile(file, targetDir, aliasMap, config);
|
|
99
|
+
if (writtenPath) {
|
|
100
|
+
writtenFiles.push(writtenPath);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Requirement 3.6 — update installedSounds in config
|
|
104
|
+
updatedConfig.installedSounds[soundName] = {
|
|
105
|
+
files: writtenFiles,
|
|
106
|
+
installedAt: new Date().toISOString(),
|
|
107
|
+
};
|
|
108
|
+
console.log(`✔ Added ${soundName}`);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
// Requirement 3.7 — handle HTTP errors with sound name and status code
|
|
112
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
113
|
+
console.error(`✘ ${message}`);
|
|
114
|
+
process.exit(2);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Write updated config once after all sounds are processed
|
|
118
|
+
ConfigManager.write(projectRoot, updatedConfig);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=add.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AA+EA,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2CpE"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import * as ConfigManager from "../core/config.js";
|
|
4
|
+
import { fetchItem } from "../core/registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the expected local file path for a registry file based on its type
|
|
7
|
+
* and the project config.
|
|
8
|
+
*/
|
|
9
|
+
function resolveLocalPath(file, config, projectRoot) {
|
|
10
|
+
const fileName = basename(file.path);
|
|
11
|
+
if (file.type === "registry:hook") {
|
|
12
|
+
const hooksDir = join(dirname(config.libDir), "hooks");
|
|
13
|
+
return join(projectRoot, hooksDir, fileName);
|
|
14
|
+
}
|
|
15
|
+
if (file.type === "registry:lib") {
|
|
16
|
+
const normalised = file.path.replace(/\\/g, "/");
|
|
17
|
+
if (normalised.includes("/sounds/")) {
|
|
18
|
+
return join(projectRoot, config.soundDir, fileName);
|
|
19
|
+
}
|
|
20
|
+
return join(projectRoot, config.libDir, fileName);
|
|
21
|
+
}
|
|
22
|
+
return join(projectRoot, config.soundDir, fileName);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read local file content, returning null if the file cannot be read.
|
|
26
|
+
*/
|
|
27
|
+
function readLocalFile(filePath) {
|
|
28
|
+
try {
|
|
29
|
+
return readFileSync(filePath, "utf-8");
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Compare registry file content against local file content.
|
|
37
|
+
* Returns true if the files differ (ignoring import path differences
|
|
38
|
+
* is not feasible here — we compare raw registry content against local).
|
|
39
|
+
*
|
|
40
|
+
* Since local files have rewritten imports, we only compare the sound
|
|
41
|
+
* module files (the primary files in /sounds/) by checking if the local
|
|
42
|
+
* file exists and has different content length or key data sections.
|
|
43
|
+
* For a pragmatic approach, we compare the full content of each registry
|
|
44
|
+
* file against the local file. Note: import-rewritten files will always
|
|
45
|
+
* show as different if aliases differ from registry defaults, but this
|
|
46
|
+
* is the expected behavior for detecting upstream changes.
|
|
47
|
+
*/
|
|
48
|
+
function hasChanges(registryFiles, config, projectRoot) {
|
|
49
|
+
for (const file of registryFiles) {
|
|
50
|
+
const localPath = resolveLocalPath(file, config, projectRoot);
|
|
51
|
+
const localContent = readLocalFile(localPath);
|
|
52
|
+
if (localContent === null) {
|
|
53
|
+
// File missing locally — counts as a difference
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
// Compare registry content against local content
|
|
57
|
+
if (file.content !== localContent) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
export async function diffCommand(projectRoot) {
|
|
64
|
+
// Config must exist
|
|
65
|
+
if (!ConfigManager.exists(projectRoot)) {
|
|
66
|
+
console.error("Configuration not found. Run 'audx init' first.");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const config = ConfigManager.read(projectRoot);
|
|
70
|
+
const installedNames = Object.keys(config.installedSounds);
|
|
71
|
+
if (installedNames.length === 0) {
|
|
72
|
+
console.log("No sounds installed.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const changedSounds = [];
|
|
76
|
+
for (const soundName of installedNames) {
|
|
77
|
+
try {
|
|
78
|
+
// Requirement 10.1 — fetch current registry item for each installed sound
|
|
79
|
+
const item = await fetchItem(config.registryUrl, soundName);
|
|
80
|
+
// Compare fetched file content against local files
|
|
81
|
+
if (hasChanges(item.files, config, projectRoot)) {
|
|
82
|
+
changedSounds.push(soundName);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
// Requirement 10.7 — continue on individual fetch failures with warning
|
|
87
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
88
|
+
console.warn(`⚠ Could not check '${soundName}': ${message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Requirement 10.2 — display changed sound names
|
|
92
|
+
if (changedSounds.length > 0) {
|
|
93
|
+
console.log("Sounds with available updates:");
|
|
94
|
+
for (const name of changedSounds) {
|
|
95
|
+
console.log(` • ${name}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Requirement 10.3 — all up to date message
|
|
100
|
+
console.log("All sounds are up to date.");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `audx generate "<prompt>"` command handler.
|
|
3
|
+
*
|
|
4
|
+
* Accepts a prompt string, optional --name and --duration flags.
|
|
5
|
+
* Generates a sound via the API, writes a Sound_Module file, and
|
|
6
|
+
* updates the config manifest.
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateCommand(prompt: string, options: {
|
|
9
|
+
name?: string;
|
|
10
|
+
duration?: string;
|
|
11
|
+
}, projectRoot: string): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AA+CA;;;;;;GAMG;AACH,wBAAsB,eAAe,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAC7C,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAgEf"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as ConfigManager from "../core/config.js";
|
|
4
|
+
import { generateSound } from "../core/registry.js";
|
|
5
|
+
import { deriveKebabName, encodeAudioToDataUri } from "../core/utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Convert a kebab-case name to a camelCase variable name suffixed with "Audio".
|
|
8
|
+
* e.g. "laser-blast" → "laserBlastAudio"
|
|
9
|
+
*/
|
|
10
|
+
function toCamelCaseAudioVar(kebab) {
|
|
11
|
+
const camel = kebab.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
12
|
+
return `${camel}Audio`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the Sound_Module TypeScript source for a generated sound.
|
|
16
|
+
*/
|
|
17
|
+
function buildSoundModule(name, dataUri, duration) {
|
|
18
|
+
const varName = toCamelCaseAudioVar(name);
|
|
19
|
+
const dur = duration ?? 2;
|
|
20
|
+
const lines = [
|
|
21
|
+
`import type { AudioAsset } from "@/lib/audio-types";`,
|
|
22
|
+
``,
|
|
23
|
+
`export const ${varName}: AudioAsset = {`,
|
|
24
|
+
` name: "${name}",`,
|
|
25
|
+
` dataUri:`,
|
|
26
|
+
` "${dataUri}",`,
|
|
27
|
+
` duration: ${dur},`,
|
|
28
|
+
` format: "mp3",`,
|
|
29
|
+
` license: "MIT",`,
|
|
30
|
+
` author: "Generated",`,
|
|
31
|
+
`};`,
|
|
32
|
+
``,
|
|
33
|
+
];
|
|
34
|
+
return lines.join("\n");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* `audx generate "<prompt>"` command handler.
|
|
38
|
+
*
|
|
39
|
+
* Accepts a prompt string, optional --name and --duration flags.
|
|
40
|
+
* Generates a sound via the API, writes a Sound_Module file, and
|
|
41
|
+
* updates the config manifest.
|
|
42
|
+
*/
|
|
43
|
+
export async function generateCommand(prompt, options, projectRoot) {
|
|
44
|
+
// Requirement 8.8 — config must exist
|
|
45
|
+
if (!ConfigManager.exists(projectRoot)) {
|
|
46
|
+
console.error("Configuration not found. Run 'audx init' first.");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const config = ConfigManager.read(projectRoot);
|
|
50
|
+
// Requirement 8.2 / 8.3 — derive or use provided name
|
|
51
|
+
const soundName = options.name ?? deriveKebabName(prompt);
|
|
52
|
+
// Build API params
|
|
53
|
+
const params = { text: prompt };
|
|
54
|
+
// Requirement 8.4 — optional duration
|
|
55
|
+
if (options.duration !== undefined) {
|
|
56
|
+
const dur = Number(options.duration);
|
|
57
|
+
if (Number.isNaN(dur) || dur < 0.5 || dur > 22) {
|
|
58
|
+
console.error("Duration must be a number between 0.5 and 22.");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
params.duration_seconds = dur;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
// Requirement 8.1 — POST to generation API
|
|
65
|
+
console.log(`Generating sound "${soundName}" from prompt: "${prompt}"...`);
|
|
66
|
+
const audioBuffer = await generateSound(config.registryUrl, params);
|
|
67
|
+
// Requirement 8.5 — encode as base64 data URI and create Sound_Module
|
|
68
|
+
const dataUri = encodeAudioToDataUri(audioBuffer);
|
|
69
|
+
const moduleContent = buildSoundModule(soundName, dataUri, params.duration_seconds);
|
|
70
|
+
// Write to soundDir
|
|
71
|
+
const targetDir = join(projectRoot, config.soundDir);
|
|
72
|
+
mkdirSync(targetDir, { recursive: true });
|
|
73
|
+
const filePath = join(targetDir, `${soundName}.ts`);
|
|
74
|
+
writeFileSync(filePath, moduleContent, "utf-8");
|
|
75
|
+
// Requirement 8.6 — update installedSounds in config
|
|
76
|
+
const updatedConfig = {
|
|
77
|
+
...config,
|
|
78
|
+
installedSounds: {
|
|
79
|
+
...config.installedSounds,
|
|
80
|
+
[soundName]: {
|
|
81
|
+
files: [filePath],
|
|
82
|
+
installedAt: new Date().toISOString(),
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
ConfigManager.write(projectRoot, updatedConfig);
|
|
87
|
+
console.log(`✔ Generated and installed ${soundName}`);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
// Requirement 8.7 — handle API errors
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
92
|
+
console.error(`✘ ${message}`);
|
|
93
|
+
process.exit(2);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAoDA,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8CpE"}
|