ui-chan-mcp 0.5.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-plugin/marketplace.json +13 -0
- package/.claude-plugin/plugin.json +6 -0
- package/.env.example +13 -0
- package/LICENSE +36 -0
- package/README.md +214 -0
- package/agents/mode.md +47 -0
- package/agents/talk.md +23 -0
- package/bin/ui-chan-mcp.mjs +11 -0
- package/bin/ui-chan-node +35 -0
- package/bin/ui-chan.mjs +350 -0
- package/context/AFFINITY.md +94 -0
- package/context/SOUL.md +79 -0
- package/context/VOCABULARY.md +108 -0
- package/cue.schema.json +109 -0
- package/cues/default.json +21 -0
- package/cues/emo_anger.json +14 -0
- package/cues/emo_anger_hi.json +19 -0
- package/cues/emo_anger_lo.json +16 -0
- package/cues/emo_antic.json +18 -0
- package/cues/emo_antic_hi.json +16 -0
- package/cues/emo_antic_lo.json +6 -0
- package/cues/emo_disgust.json +19 -0
- package/cues/emo_disgust_hi.json +20 -0
- package/cues/emo_disgust_lo.json +6 -0
- package/cues/emo_fear.json +19 -0
- package/cues/emo_fear_hi.json +19 -0
- package/cues/emo_fear_lo.json +19 -0
- package/cues/emo_joy.json +17 -0
- package/cues/emo_joy_hi.json +15 -0
- package/cues/emo_joy_lo.json +13 -0
- package/cues/emo_sad.json +15 -0
- package/cues/emo_sad_hi.json +18 -0
- package/cues/emo_sad_lo.json +14 -0
- package/cues/emo_surprise.json +15 -0
- package/cues/emo_surprise_hi.json +14 -0
- package/cues/emo_surprise_lo.json +11 -0
- package/cues/emo_trust.json +16 -0
- package/cues/emo_trust_hi.json +20 -0
- package/cues/emo_trust_lo.json +6 -0
- package/cues/idling_doze_1.json +9 -0
- package/cues/idling_doze_2.json +9 -0
- package/cues/idling_doze_3.json +9 -0
- package/cues/idling_giggle_1.json +9 -0
- package/cues/idling_giggle_2.json +9 -0
- package/cues/idling_lookaround_1.json +10 -0
- package/cues/idling_lookaround_2.json +10 -0
- package/cues/idling_lookaround_3.json +10 -0
- package/cues/idling_ponder.json +9 -0
- package/cues/idling_sigh_1.json +11 -0
- package/cues/idling_sigh_2.json +11 -0
- package/cues/idling_yawn_1.json +9 -0
- package/cues/idling_yawn_2.json +12 -0
- package/cues/idling_yawn_3.json +12 -0
- package/cues/mix_anger_antic.json +16 -0
- package/cues/mix_antic_joy.json +16 -0
- package/cues/mix_disgust_anger.json +17 -0
- package/cues/mix_fear_surprise.json +18 -0
- package/cues/mix_joy_trust.json +19 -0
- package/cues/mix_joy_trust_hi.json +18 -0
- package/cues/mix_sad_disgust.json +12 -0
- package/cues/mix_surprise_sad.json +21 -0
- package/cues/mix_surprise_sad_hi.json +14 -0
- package/cues/mix_trust_fear.json +18 -0
- package/cues/pose_arms_crossed.json +9 -0
- package/cues/pose_banzai.json +17 -0
- package/cues/pose_smug_arms.json +11 -0
- package/cues/pose_smug_hips.json +11 -0
- package/cues/pose_think.json +8 -0
- package/cues/pose_umbrella.json +9 -0
- package/cues/self_guilt.json +20 -0
- package/cues/self_pride.json +10 -0
- package/cues/self_shame.json +19 -0
- package/cues/self_shy.json +16 -0
- package/cues/self_shy_hi.json +17 -0
- package/cues/sys_address.json +9 -0
- package/cues/sys_awkward.json +18 -0
- package/cues/sys_beam.json +18 -0
- package/cues/sys_blank.json +11 -0
- package/cues/sys_care.json +18 -0
- package/cues/sys_confused.json +12 -0
- package/cues/sys_dazed.json +10 -0
- package/cues/sys_dread.json +21 -0
- package/cues/sys_fluster.json +20 -0
- package/cues/sys_intro.json +15 -0
- package/cues/sys_laugh.json +20 -0
- package/cues/sys_neutral.json +5 -0
- package/cues/sys_present.json +9 -0
- package/cues/sys_rain.json +15 -0
- package/cues/sys_refuse.json +18 -0
- package/cues/sys_relief.json +15 -0
- package/cues/sys_sleepy.json +8 -0
- package/cues/sys_smirk.json +10 -0
- package/cues/sys_stream.json +15 -0
- package/cues/sys_success.json +15 -0
- package/cues/sys_think.json +9 -0
- package/dist/app/assets.js +54 -0
- package/dist/app/cues.js +130 -0
- package/dist/app/editor-main.js +199 -0
- package/dist/app/editor-preload.js +17 -0
- package/dist/app/main.js +527 -0
- package/dist/app/persona.js +136 -0
- package/dist/app/preload.js +14 -0
- package/dist/app/state.js +850 -0
- package/dist/app/tts.js +257 -0
- package/dist/mcp-server.js +408 -0
- package/dist/renderer/bundle.js +17983 -0
- package/dist/renderer/editor.html +150 -0
- package/dist/renderer/editor.js +17917 -0
- package/dist/renderer/index.html +425 -0
- package/dist/shared/paths.js +169 -0
- package/dist/shared/set-cue-schema.js +66 -0
- package/dist/shared/types.js +14 -0
- package/docs/CLIENTS.md +110 -0
- package/docs/CUE_AUTHORING.md +76 -0
- package/docs/DEVELOPMENT.md +129 -0
- package/docs/PERSONA.md +61 -0
- package/docs/PSD_LAYERS.md +123 -0
- package/docs/README.md +43 -0
- package/docs/SETUP.html +684 -0
- package/docs/STYLE.md +107 -0
- package/docs/TOOLS.md +33 -0
- package/docs/TROUBLESHOOTING.md +122 -0
- package/docs/TTS.md +61 -0
- package/docs/design/CUE_CATALOG.md +259 -0
- package/docs/images/faces.png +0 -0
- package/docs/images/panel.png +0 -0
- package/hooks/fire-event.js +14 -0
- package/hooks/hooks.json +77 -0
- package/hooks/lib/mascot.js +76 -0
- package/hooks/notify.js +15 -0
- package/hooks/reaction.js +46 -0
- package/hooks/session-start.js +80 -0
- package/package.json +96 -0
- package/persona/ui-chan.md +111 -0
- package/plugins/hermes/ui-chan/__init__.py +98 -0
- package/plugins/hermes/ui-chan/plugin.yaml +7 -0
- package/plugins/opencode/ui-chan.mjs +105 -0
- package/skills/beam/SKILL.md +37 -0
- package/skills/eli14/SKILL.md +139 -0
- package/skills/eli14/references/base.html +495 -0
- package/skills/mode/SKILL.md +67 -0
- package/skills/talk/SKILL.md +27 -0
- package/tools/setup/check-package.mjs +47 -0
- package/tools/setup/clients.mjs +399 -0
- package/tools/setup/doctor.mjs +105 -0
- package/tools/setup/home.mjs +126 -0
- package/tools/setup/prompt.mjs +134 -0
- package/tools/setup/update-check.mjs +8 -0
- package/tools/setup/update.mjs +350 -0
- package/tools/stop-app.mjs +22 -0
- package/ui-chan.config.json +973 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.findPsd = findPsd;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
/** First `.psd` (alphabetical) in the first assets dir that has one, or null.
|
|
40
|
+
* Shared by the mascot app (main.ts) and the Cue editor (editor-main.ts) so
|
|
41
|
+
* both discover the mascot PSD the same way. */
|
|
42
|
+
function findPsd(assetsDirs) {
|
|
43
|
+
for (const dir of Array.isArray(assetsDirs) ? assetsDirs : [assetsDirs]) {
|
|
44
|
+
if (!fs.existsSync(dir))
|
|
45
|
+
continue;
|
|
46
|
+
const psd = fs
|
|
47
|
+
.readdirSync(dir)
|
|
48
|
+
.filter((f) => f.toLowerCase().endsWith('.psd'))
|
|
49
|
+
.sort();
|
|
50
|
+
if (psd.length > 0)
|
|
51
|
+
return path.join(dir, psd[0]);
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
package/dist/app/cues.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.loadCues = loadCues;
|
|
40
|
+
exports.validateCueObject = validateCueObject;
|
|
41
|
+
exports.watchCues = watchCues;
|
|
42
|
+
exports.extractCueVoice = extractCueVoice;
|
|
43
|
+
const fs = __importStar(require("node:fs"));
|
|
44
|
+
const path = __importStar(require("node:path"));
|
|
45
|
+
const _2020_js_1 = __importDefault(require("ajv/dist/2020.js"));
|
|
46
|
+
const types_1 = require("../shared/types");
|
|
47
|
+
let validateCue = null;
|
|
48
|
+
function getValidator(schemaPath) {
|
|
49
|
+
if (!validateCue) {
|
|
50
|
+
const ajv = new _2020_js_1.default({ allErrors: true, strict: false });
|
|
51
|
+
validateCue = ajv.compile(JSON.parse(fs.readFileSync(schemaPath, 'utf-8')));
|
|
52
|
+
}
|
|
53
|
+
return validateCue;
|
|
54
|
+
}
|
|
55
|
+
/** One `<name>.json` = one Cue: a flat select/show/hide/blink/voice file,
|
|
56
|
+
* fully self-contained and validated against cue.schema.json — the schema
|
|
57
|
+
* file itself is the source of truth, not a hand-duplicated set of TS
|
|
58
|
+
* constraints. No inheritance, no bundling. */
|
|
59
|
+
function loadCues(dirs, schemaPath) {
|
|
60
|
+
const set = { cues: {}, errors: [] };
|
|
61
|
+
const validate = getValidator(schemaPath);
|
|
62
|
+
// Several dirs may contribute (packaged cues + the user's `~/.ui-chan/cues`).
|
|
63
|
+
// Later dirs win on a shared name, so a user override never has to fork the
|
|
64
|
+
// whole catalog — see src/shared/paths.ts.
|
|
65
|
+
const list = Array.isArray(dirs) ? dirs : [dirs];
|
|
66
|
+
if (list.length === 0)
|
|
67
|
+
set.errors.push('no cues directory configured');
|
|
68
|
+
for (const dir of list) {
|
|
69
|
+
if (!fs.existsSync(dir)) {
|
|
70
|
+
set.errors.push(`cues directory not found: ${dir}`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
for (const file of fs
|
|
74
|
+
.readdirSync(dir)
|
|
75
|
+
.filter((f) => f.endsWith('.json'))
|
|
76
|
+
.sort()) {
|
|
77
|
+
const name = path.basename(file, '.json');
|
|
78
|
+
try {
|
|
79
|
+
const raw = JSON.parse(fs.readFileSync(path.join(dir, file), 'utf-8'));
|
|
80
|
+
if (!validate(raw)) {
|
|
81
|
+
const detail = (validate.errors ?? [])
|
|
82
|
+
.map((e) => `${e.instancePath || '/'} ${e.message}`)
|
|
83
|
+
.join('; ');
|
|
84
|
+
set.errors.push(`${file}: ${detail}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
set.cues[name] = raw;
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
set.errors.push(`${file}: ${e instanceof Error ? e.message : e}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (!set.cues[types_1.DEFAULT_CUE_NAME]) {
|
|
95
|
+
set.errors.push(`no "${types_1.DEFAULT_CUE_NAME}" cue found in ${list.join(', ')} — falling back to an empty default (no select/show/hide, blink off)`);
|
|
96
|
+
set.cues[types_1.DEFAULT_CUE_NAME] = {};
|
|
97
|
+
}
|
|
98
|
+
return set;
|
|
99
|
+
}
|
|
100
|
+
/** Validate one Cue object against cue.schema.json (used by the editor before
|
|
101
|
+
* writing a file). Returns null on success or a joined error string. */
|
|
102
|
+
function validateCueObject(obj, schemaPath) {
|
|
103
|
+
const validate = getValidator(schemaPath);
|
|
104
|
+
if (validate(obj))
|
|
105
|
+
return null;
|
|
106
|
+
return (validate.errors ?? []).map((e) => `${e.instancePath || '/'} ${e.message}`).join('; ');
|
|
107
|
+
}
|
|
108
|
+
function watchCues(dirs, onChange) {
|
|
109
|
+
let timer = null;
|
|
110
|
+
const fire = () => {
|
|
111
|
+
if (timer)
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
timer = setTimeout(() => {
|
|
114
|
+
timer = null;
|
|
115
|
+
onChange();
|
|
116
|
+
}, 300);
|
|
117
|
+
};
|
|
118
|
+
for (const dir of Array.isArray(dirs) ? dirs : [dirs]) {
|
|
119
|
+
if (fs.existsSync(dir))
|
|
120
|
+
fs.watch(dir, fire);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function extractCueVoice(set) {
|
|
124
|
+
const out = {};
|
|
125
|
+
for (const [name, cue] of Object.entries(set.cues)) {
|
|
126
|
+
if (cue.voice)
|
|
127
|
+
out[name] = cue.voice;
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("node:fs"));
|
|
37
|
+
const path = __importStar(require("node:path"));
|
|
38
|
+
const electron_1 = require("electron");
|
|
39
|
+
const paths_1 = require("../shared/paths");
|
|
40
|
+
const types_1 = require("../shared/types");
|
|
41
|
+
const assets_1 = require("./assets");
|
|
42
|
+
const cues_1 = require("./cues");
|
|
43
|
+
const tts_1 = require("./tts");
|
|
44
|
+
// Standalone Cue editor window. A separate Electron entry from the mascot app
|
|
45
|
+
// (main.ts): opaque + framed, self-renders the PSD for preview, reads/writes
|
|
46
|
+
// cues/*.json directly, and previews voice via its own TTS client. Runs
|
|
47
|
+
// independently of the mascot — saving a cue is picked up live by a running
|
|
48
|
+
// mascot through its watchCues() hot-reload.
|
|
49
|
+
const projectRoot = path.resolve(__dirname, '..', '..');
|
|
50
|
+
// TTS credentials for "試し喋り" live in .env / env vars (never in the config).
|
|
51
|
+
(0, paths_1.loadEnvFiles)(projectRoot);
|
|
52
|
+
const paths = (0, paths_1.resolvePaths)(projectRoot);
|
|
53
|
+
const config = paths.config;
|
|
54
|
+
const assetsDirs = paths.assetsDirs;
|
|
55
|
+
const cueDirs = paths.cueDirs;
|
|
56
|
+
const cueSchemaPath = paths.cueSchemaFile;
|
|
57
|
+
const tts = config.tts?.enabled ? new tts_1.VoiSonaTalkClient(config.tts) : null;
|
|
58
|
+
const CUE_NAME_RE = /^[A-Za-z0-9_-]+$/;
|
|
59
|
+
/** Resolve `<name>.json` inside one cue dir, rejecting bad names / escapes. */
|
|
60
|
+
function cueFileIn(dir, name) {
|
|
61
|
+
if (!CUE_NAME_RE.test(name) || name === types_1.DEFAULT_CUE_NAME)
|
|
62
|
+
return null;
|
|
63
|
+
const p = path.join(dir, `${name}.json`);
|
|
64
|
+
if (p !== path.join(dir, path.basename(p)) || !p.startsWith(dir + path.sep))
|
|
65
|
+
return null;
|
|
66
|
+
return p;
|
|
67
|
+
}
|
|
68
|
+
/** Where an edit is saved — the user's dir when there is one, so an update of
|
|
69
|
+
* the package never overwrites hand-authored cues (shared/paths.ts). */
|
|
70
|
+
function cueWritePath(name) {
|
|
71
|
+
return cueFileIn(paths.cueWriteDir, name);
|
|
72
|
+
}
|
|
73
|
+
/** The file a cue currently lives in, searched with the same precedence
|
|
74
|
+
* loadCues() uses (last dir wins). */
|
|
75
|
+
function cueExistingPath(name) {
|
|
76
|
+
for (const dir of [...cueDirs].reverse()) {
|
|
77
|
+
const p = cueFileIn(dir, name);
|
|
78
|
+
if (p && fs.existsSync(p))
|
|
79
|
+
return p;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
function createWindow() {
|
|
84
|
+
const win = new electron_1.BrowserWindow({
|
|
85
|
+
width: 1180,
|
|
86
|
+
height: 800,
|
|
87
|
+
minWidth: 900,
|
|
88
|
+
minHeight: 600,
|
|
89
|
+
title: '雨衣ちゃんのデバッグルーム',
|
|
90
|
+
webPreferences: {
|
|
91
|
+
preload: path.join(__dirname, 'editor-preload.js'),
|
|
92
|
+
contextIsolation: true,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
win.loadFile(path.join(projectRoot, 'dist', 'renderer', 'editor.html'));
|
|
96
|
+
}
|
|
97
|
+
electron_1.app.whenReady().then(() => {
|
|
98
|
+
createWindow();
|
|
99
|
+
electron_1.app.on('activate', () => {
|
|
100
|
+
if (electron_1.BrowserWindow.getAllWindows().length === 0)
|
|
101
|
+
createWindow();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
electron_1.app.on('window-all-closed', () => electron_1.app.quit());
|
|
105
|
+
electron_1.ipcMain.handle('editor:get-init', () => ({
|
|
106
|
+
psdAvailable: (0, assets_1.findPsd)(assetsDirs) !== null,
|
|
107
|
+
lipSync: config.lipSync ?? null,
|
|
108
|
+
}));
|
|
109
|
+
electron_1.ipcMain.handle('editor:read-psd', () => {
|
|
110
|
+
const psd = (0, assets_1.findPsd)(assetsDirs);
|
|
111
|
+
return psd ? fs.readFileSync(psd) : null;
|
|
112
|
+
});
|
|
113
|
+
electron_1.ipcMain.handle('editor:list-cues', () => {
|
|
114
|
+
const { cues } = (0, cues_1.loadCues)(cueDirs, cueSchemaPath);
|
|
115
|
+
return Object.entries(cues)
|
|
116
|
+
.filter(([name]) => name !== types_1.DEFAULT_CUE_NAME)
|
|
117
|
+
.map(([name, cue]) => ({
|
|
118
|
+
name,
|
|
119
|
+
label: cue.label,
|
|
120
|
+
internal: cue.internal ?? false,
|
|
121
|
+
description: cue.description,
|
|
122
|
+
}))
|
|
123
|
+
.sort((a, b) => Number(a.internal) - Number(b.internal) || a.name.localeCompare(b.name));
|
|
124
|
+
});
|
|
125
|
+
electron_1.ipcMain.handle('editor:read-cue', (_ev, name) => {
|
|
126
|
+
const p = cueExistingPath(name);
|
|
127
|
+
if (!p)
|
|
128
|
+
return null;
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
electron_1.ipcMain.handle('editor:read-default', () => {
|
|
137
|
+
const p = [...cueDirs]
|
|
138
|
+
.reverse()
|
|
139
|
+
.map((d) => path.join(d, `${types_1.DEFAULT_CUE_NAME}.json`))
|
|
140
|
+
.find((f) => fs.existsSync(f));
|
|
141
|
+
if (!p)
|
|
142
|
+
return {};
|
|
143
|
+
try {
|
|
144
|
+
return JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return {};
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
electron_1.ipcMain.handle('editor:write-cue', (_ev, name, cue) => {
|
|
151
|
+
const p = cueWritePath(name);
|
|
152
|
+
if (!p)
|
|
153
|
+
return { ok: false, error: `不正なCue名: "${name}"` };
|
|
154
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
155
|
+
const err = (0, cues_1.validateCueObject)(cue, cueSchemaPath);
|
|
156
|
+
if (err)
|
|
157
|
+
return { ok: false, error: `スキーマ検証エラー: ${err}` };
|
|
158
|
+
try {
|
|
159
|
+
fs.writeFileSync(p, `${JSON.stringify(cue, null, 2)}\n`, 'utf-8');
|
|
160
|
+
return { ok: true };
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
electron_1.ipcMain.handle('editor:delete-cue', (_ev, name) => {
|
|
167
|
+
if (!CUE_NAME_RE.test(name) || name === types_1.DEFAULT_CUE_NAME)
|
|
168
|
+
return { ok: false, error: `不正なCue名: "${name}"` };
|
|
169
|
+
const p = cueExistingPath(name);
|
|
170
|
+
if (!p)
|
|
171
|
+
return { ok: false, error: `存在しません: ${name}` };
|
|
172
|
+
try {
|
|
173
|
+
fs.unlinkSync(p);
|
|
174
|
+
return { ok: true };
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
// CueSequence names (IdlingCues + FidgetCues) whose steps reference this
|
|
181
|
+
// cue — a delete-safety warning so removing a cue used by a sequence doesn't
|
|
182
|
+
// silently break it.
|
|
183
|
+
electron_1.ipcMain.handle('editor:cue-refs', (_ev, name) => {
|
|
184
|
+
const pools = [
|
|
185
|
+
{ label: 'idling', items: config.idle?.idlingCues?.items },
|
|
186
|
+
{ label: 'poke', items: config.interactions?.poke },
|
|
187
|
+
];
|
|
188
|
+
const refs = [];
|
|
189
|
+
for (const { label, items } of pools) {
|
|
190
|
+
(items ?? []).forEach((item, i) => {
|
|
191
|
+
if ((item.steps ?? []).some((s) => s.cue === name)) {
|
|
192
|
+
refs.push(`${label}:${item.name ?? `#${i}`}`);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return refs;
|
|
197
|
+
});
|
|
198
|
+
electron_1.ipcMain.handle('editor:list-styles', () => (tts ? tts.listStyles() : null));
|
|
199
|
+
electron_1.ipcMain.handle('editor:synthesize', (_ev, text, voice) => tts ? tts.synthesizeWithVoice(text, voice) : null);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const electron_1 = require("electron");
|
|
4
|
+
// Bridge for the Cue editor renderer (editor.ts). Mirrors preload.ts's shape
|
|
5
|
+
// but exposes editor-only, file-mutating operations under window.uiEditor.
|
|
6
|
+
electron_1.contextBridge.exposeInMainWorld('uiEditor', {
|
|
7
|
+
getInit: () => electron_1.ipcRenderer.invoke('editor:get-init'),
|
|
8
|
+
readPsd: () => electron_1.ipcRenderer.invoke('editor:read-psd'),
|
|
9
|
+
listCues: () => electron_1.ipcRenderer.invoke('editor:list-cues'),
|
|
10
|
+
readCue: (name) => electron_1.ipcRenderer.invoke('editor:read-cue', name),
|
|
11
|
+
readDefault: () => electron_1.ipcRenderer.invoke('editor:read-default'),
|
|
12
|
+
writeCue: (name, cue) => electron_1.ipcRenderer.invoke('editor:write-cue', name, cue),
|
|
13
|
+
deleteCue: (name) => electron_1.ipcRenderer.invoke('editor:delete-cue', name),
|
|
14
|
+
cueRefs: (name) => electron_1.ipcRenderer.invoke('editor:cue-refs', name),
|
|
15
|
+
listStyles: () => electron_1.ipcRenderer.invoke('editor:list-styles'),
|
|
16
|
+
synthesize: (text, voice) => electron_1.ipcRenderer.invoke('editor:synthesize', text, voice),
|
|
17
|
+
});
|