miladyai 2.0.0-alpha.27
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/dist/_virtual/_rolldown/runtime.js +7 -0
- package/dist/actions/emote.js +64 -0
- package/dist/actions/restart.js +81 -0
- package/dist/actions/send-message.js +152 -0
- package/dist/agent-admin-routes.js +82 -0
- package/dist/agent-lifecycle-routes.js +79 -0
- package/dist/agent-transfer-routes.js +102 -0
- package/dist/api/agent-admin-routes.js +82 -0
- package/dist/api/agent-lifecycle-routes.js +79 -0
- package/dist/api/agent-transfer-routes.js +102 -0
- package/dist/api/apps-hyperscape-routes.js +58 -0
- package/dist/api/apps-routes.js +114 -0
- package/dist/api/auth-routes.js +56 -0
- package/dist/api/autonomy-routes.js +44 -0
- package/dist/api/bug-report-routes.js +111 -0
- package/dist/api/character-routes.js +195 -0
- package/dist/api/cloud-routes.js +330 -0
- package/dist/api/cloud-status-routes.js +155 -0
- package/dist/api/compat-utils.js +111 -0
- package/dist/api/database.js +735 -0
- package/dist/api/diagnostics-routes.js +205 -0
- package/dist/api/drop-service.js +134 -0
- package/dist/api/early-logs.js +86 -0
- package/dist/api/http-helpers.js +131 -0
- package/dist/api/knowledge-routes.js +534 -0
- package/dist/api/memory-bounds.js +71 -0
- package/dist/api/models-routes.js +28 -0
- package/dist/api/og-tracker.js +36 -0
- package/dist/api/permissions-routes.js +109 -0
- package/dist/api/plugin-validation.js +198 -0
- package/dist/api/provider-switch-config.js +41 -0
- package/dist/api/registry-routes.js +86 -0
- package/dist/api/registry-service.js +164 -0
- package/dist/api/sandbox-routes.js +1112 -0
- package/dist/api/server.js +7949 -0
- package/dist/api/subscription-routes.js +172 -0
- package/dist/api/terminal-run-limits.js +24 -0
- package/dist/api/training-routes.js +158 -0
- package/dist/api/trajectory-routes.js +300 -0
- package/dist/api/trigger-routes.js +246 -0
- package/dist/api/twitter-verify.js +134 -0
- package/dist/api/tx-service.js +108 -0
- package/dist/api/wallet-routes.js +266 -0
- package/dist/api/wallet.js +568 -0
- package/dist/api/whatsapp-routes.js +182 -0
- package/dist/api/zip-utils.js +109 -0
- package/dist/apps-hyperscape-routes.js +58 -0
- package/dist/apps-routes.js +114 -0
- package/dist/ascii.js +20 -0
- package/dist/auth/anthropic.js +44 -0
- package/dist/auth/apply-stealth.js +41 -0
- package/dist/auth/claude-code-stealth.js +78 -0
- package/dist/auth/credentials.js +156 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/openai-codex.js +66 -0
- package/dist/auth/types.js +9 -0
- package/dist/auth-routes.js +56 -0
- package/dist/autonomy-routes.js +44 -0
- package/dist/bug-report-routes.js +111 -0
- package/dist/build-info.json +6 -0
- package/dist/character-routes.js +195 -0
- package/dist/cli/argv.js +63 -0
- package/dist/cli/banner.js +34 -0
- package/dist/cli/cli-name.js +21 -0
- package/dist/cli/cli-utils.js +16 -0
- package/dist/cli/git-commit.js +78 -0
- package/dist/cli/parse-duration.js +15 -0
- package/dist/cli/plugins-cli.js +590 -0
- package/dist/cli/profile-utils.js +9 -0
- package/dist/cli/profile.js +95 -0
- package/dist/cli/program/build-program.js +17 -0
- package/dist/cli/program/command-registry.js +23 -0
- package/dist/cli/program/help.js +47 -0
- package/dist/cli/program/preaction.js +33 -0
- package/dist/cli/program/register.config.js +106 -0
- package/dist/cli/program/register.configure.js +20 -0
- package/dist/cli/program/register.dashboard.js +124 -0
- package/dist/cli/program/register.models.js +23 -0
- package/dist/cli/program/register.setup.js +36 -0
- package/dist/cli/program/register.start.js +22 -0
- package/dist/cli/program/register.subclis.js +70 -0
- package/dist/cli/program/register.tui.js +163 -0
- package/dist/cli/program/register.update.js +154 -0
- package/dist/cli/program.js +3 -0
- package/dist/cli/run-main.js +37 -0
- package/dist/cli/version.js +7 -0
- package/dist/cloud/validate-url.js +93 -0
- package/dist/cloud-routes.js +330 -0
- package/dist/cloud-status-routes.js +155 -0
- package/dist/compat-utils.js +111 -0
- package/dist/config/config.js +69 -0
- package/dist/config/env-vars.js +19 -0
- package/dist/config/includes.js +121 -0
- package/dist/config/object-utils.js +7 -0
- package/dist/config/paths.js +38 -0
- package/dist/config/plugin-auto-enable.js +231 -0
- package/dist/config/schema.js +864 -0
- package/dist/config/telegram-custom-commands.js +76 -0
- package/dist/config/zod-schema.agent-runtime.js +519 -0
- package/dist/config/zod-schema.core.js +538 -0
- package/dist/config/zod-schema.hooks.js +103 -0
- package/dist/config/zod-schema.js +488 -0
- package/dist/config/zod-schema.providers-core.js +785 -0
- package/dist/config/zod-schema.session.js +73 -0
- package/dist/core-plugins.js +37 -0
- package/dist/custom-actions.js +250 -0
- package/dist/database.js +735 -0
- package/dist/diagnostics/integration-observability.js +57 -0
- package/dist/diagnostics-routes.js +205 -0
- package/dist/drop-service.js +134 -0
- package/dist/early-logs.js +24 -0
- package/dist/eliza.js +2061 -0
- package/dist/emotes/catalog.js +271 -0
- package/dist/entry.js +40 -0
- package/dist/hooks/discovery.js +167 -0
- package/dist/hooks/eligibility.js +64 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/loader.js +147 -0
- package/dist/hooks/registry.js +55 -0
- package/dist/http-helpers.js +131 -0
- package/dist/index.js +49 -0
- package/dist/knowledge-routes.js +534 -0
- package/dist/memory-bounds.js +71 -0
- package/dist/milady-plugin.js +90 -0
- package/dist/models-routes.js +28 -0
- package/dist/onboarding-names.js +78 -0
- package/dist/onboarding-presets.js +922 -0
- package/dist/package.json +1 -0
- package/dist/permissions-routes.js +109 -0
- package/dist/plugin-validation.js +107 -0
- package/dist/plugins/whatsapp/actions.js +91 -0
- package/dist/plugins/whatsapp/index.js +16 -0
- package/dist/plugins/whatsapp/service.js +270 -0
- package/dist/provider-switch-config.js +41 -0
- package/dist/providers/admin-trust.js +46 -0
- package/dist/providers/autonomous-state.js +101 -0
- package/dist/providers/session-bridge.js +86 -0
- package/dist/providers/session-utils.js +36 -0
- package/dist/providers/simple-mode.js +50 -0
- package/dist/providers/ui-catalog.js +15 -0
- package/dist/providers/workspace-provider.js +93 -0
- package/dist/providers/workspace.js +348 -0
- package/dist/registry-routes.js +86 -0
- package/dist/registry-service.js +164 -0
- package/dist/restart.js +40 -0
- package/dist/runtime/core-plugins.js +37 -0
- package/dist/runtime/custom-actions.js +250 -0
- package/dist/runtime/eliza.js +2061 -0
- package/dist/runtime/embedding-manager-support.js +185 -0
- package/dist/runtime/embedding-manager.js +193 -0
- package/dist/runtime/embedding-presets.js +54 -0
- package/dist/runtime/embedding-state.js +8 -0
- package/dist/runtime/milady-plugin.js +90 -0
- package/dist/runtime/onboarding-names.js +78 -0
- package/dist/runtime/restart.js +40 -0
- package/dist/runtime/version.js +7 -0
- package/dist/sandbox-routes.js +1112 -0
- package/dist/security/audit-log.js +149 -0
- package/dist/security/network-policy.js +70 -0
- package/dist/server.js +7949 -0
- package/dist/services/agent-export.js +559 -0
- package/dist/services/app-manager.js +389 -0
- package/dist/services/browser-capture.js +86 -0
- package/dist/services/fallback-training-service.js +128 -0
- package/dist/services/mcp-marketplace.js +134 -0
- package/dist/services/plugin-installer.js +396 -0
- package/dist/services/plugin-manager-types.js +15 -0
- package/dist/services/registry-client-app-meta.js +144 -0
- package/dist/services/registry-client-endpoints.js +166 -0
- package/dist/services/registry-client-local.js +271 -0
- package/dist/services/registry-client-network.js +93 -0
- package/dist/services/registry-client-queries.js +70 -0
- package/dist/services/registry-client.js +157 -0
- package/dist/services/sandbox-engine.js +511 -0
- package/dist/services/sandbox-manager.js +297 -0
- package/dist/services/self-updater.js +175 -0
- package/dist/services/skill-catalog-client.js +119 -0
- package/dist/services/skill-marketplace.js +521 -0
- package/dist/services/stream-manager.js +236 -0
- package/dist/services/update-checker.js +121 -0
- package/dist/services/update-notifier.js +29 -0
- package/dist/services/version-compat.js +78 -0
- package/dist/services/whatsapp-pairing.js +196 -0
- package/dist/shared/ui-catalog-prompt.js +728 -0
- package/dist/subscription-routes.js +172 -0
- package/dist/terminal/links.js +19 -0
- package/dist/terminal/palette.js +14 -0
- package/dist/terminal/theme.js +25 -0
- package/dist/terminal-run-limits.js +24 -0
- package/dist/training-routes.js +158 -0
- package/dist/trajectory-routes.js +300 -0
- package/dist/trigger-routes.js +246 -0
- package/dist/triggers/action.js +218 -0
- package/dist/triggers/runtime.js +281 -0
- package/dist/triggers/scheduling.js +295 -0
- package/dist/triggers/types.js +5 -0
- package/dist/tui/components/assistant-message.js +76 -0
- package/dist/tui/components/chat-editor.js +34 -0
- package/dist/tui/components/embeddings-overlay.js +46 -0
- package/dist/tui/components/footer.js +60 -0
- package/dist/tui/components/index.js +15 -0
- package/dist/tui/components/modal-frame.js +45 -0
- package/dist/tui/components/modal-style.js +15 -0
- package/dist/tui/components/model-selector.js +70 -0
- package/dist/tui/components/pinned-chat-layout.js +46 -0
- package/dist/tui/components/plugins-endpoints-tab.js +196 -0
- package/dist/tui/components/plugins-installed-tab-view.js +69 -0
- package/dist/tui/components/plugins-installed-tab.js +319 -0
- package/dist/tui/components/plugins-overlay-catalog.js +81 -0
- package/dist/tui/components/plugins-overlay-data-api.js +21 -0
- package/dist/tui/components/plugins-overlay-data-shared.js +20 -0
- package/dist/tui/components/plugins-overlay-data.js +323 -0
- package/dist/tui/components/plugins-overlay.js +117 -0
- package/dist/tui/components/plugins-store-tab.js +148 -0
- package/dist/tui/components/settings-overlay.js +61 -0
- package/dist/tui/components/status-bar.js +64 -0
- package/dist/tui/components/tool-execution.js +68 -0
- package/dist/tui/components/user-message.js +22 -0
- package/dist/tui/eliza-tui-bridge.js +606 -0
- package/dist/tui/index.js +370 -0
- package/dist/tui/modal-presets.js +33 -0
- package/dist/tui/model-spec.js +46 -0
- package/dist/tui/sse-parser.js +78 -0
- package/dist/tui/theme.js +110 -0
- package/dist/tui/titlebar-spinner.js +62 -0
- package/dist/tui/tui-app.js +311 -0
- package/dist/tui/ws-client.js +215 -0
- package/dist/twitter-verify.js +134 -0
- package/dist/tx-service.js +108 -0
- package/dist/utils/exec-safety.js +17 -0
- package/dist/utils/globals.js +20 -0
- package/dist/utils/milady-root.js +61 -0
- package/dist/utils/number-parsing.js +37 -0
- package/dist/version-resolver.js +37 -0
- package/dist/version.js +7 -0
- package/dist/wallet-routes.js +266 -0
- package/dist/wallet.js +568 -0
- package/dist/whatsapp-routes.js +182 -0
- package/dist/zip-utils.js +109 -0
- package/milady.mjs +14 -0
- package/package.json +111 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { TRIGGER_SCHEMA_VERSION } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/triggers/scheduling.ts
|
|
4
|
+
const MIN_TRIGGER_INTERVAL_MS = 6e4;
|
|
5
|
+
const MAX_TRIGGER_INTERVAL_MS = 744 * 60 * 60 * 1e3;
|
|
6
|
+
const DISABLED_TRIGGER_INTERVAL_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
7
|
+
const MAX_TRIGGER_RUN_HISTORY = 100;
|
|
8
|
+
const CRON_FIELDS = 5;
|
|
9
|
+
const CRON_SCAN_WINDOW_MS = 366 * 24 * 60 * 60 * 1e3;
|
|
10
|
+
const CRON_MINUTE_MS = 6e4;
|
|
11
|
+
const CRON_RANGES = [
|
|
12
|
+
{
|
|
13
|
+
min: 0,
|
|
14
|
+
max: 59
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
min: 0,
|
|
18
|
+
max: 23
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
min: 1,
|
|
22
|
+
max: 31
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
min: 1,
|
|
26
|
+
max: 12
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
min: 0,
|
|
30
|
+
max: 6
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
function parseInteger(raw) {
|
|
34
|
+
if (!/^-?\d+$/.test(raw)) return null;
|
|
35
|
+
const value = Number(raw);
|
|
36
|
+
if (!Number.isFinite(value)) return null;
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function clamp(value, min, max) {
|
|
40
|
+
if (value < min) return min;
|
|
41
|
+
if (value > max) return max;
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function normalizeText(value) {
|
|
45
|
+
return value.trim().replace(/\s+/g, " ");
|
|
46
|
+
}
|
|
47
|
+
function parseCronPart(part, range) {
|
|
48
|
+
const output = /* @__PURE__ */ new Set();
|
|
49
|
+
const chunks = part.split(",");
|
|
50
|
+
for (const chunkRaw of chunks) {
|
|
51
|
+
const chunk = chunkRaw.trim();
|
|
52
|
+
if (!chunk) return null;
|
|
53
|
+
const stepParts = chunk.split("/");
|
|
54
|
+
if (stepParts.length > 2) return null;
|
|
55
|
+
const step = stepParts.length === 2 ? parseInteger(stepParts[1].trim()) : 1;
|
|
56
|
+
if (step === null || step <= 0) return null;
|
|
57
|
+
const base = stepParts[0].trim();
|
|
58
|
+
if (base === "*") {
|
|
59
|
+
for (let value = range.min; value <= range.max; value += step) output.add(value);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const rangeParts = base.split("-");
|
|
63
|
+
if (rangeParts.length === 1) {
|
|
64
|
+
const single = parseInteger(rangeParts[0].trim());
|
|
65
|
+
if (single === null) return null;
|
|
66
|
+
if (single < range.min || single > range.max) return null;
|
|
67
|
+
output.add(single);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (rangeParts.length !== 2) return null;
|
|
71
|
+
const start = parseInteger(rangeParts[0].trim());
|
|
72
|
+
const end = parseInteger(rangeParts[1].trim());
|
|
73
|
+
if (start === null || end === null) return null;
|
|
74
|
+
if (start > end) return null;
|
|
75
|
+
if (start < range.min || end > range.max) return null;
|
|
76
|
+
for (let value = start; value <= end; value += step) output.add(value);
|
|
77
|
+
}
|
|
78
|
+
return output.size > 0 ? output : null;
|
|
79
|
+
}
|
|
80
|
+
function parseCronExpression(expression) {
|
|
81
|
+
const trimmed = expression.trim();
|
|
82
|
+
if (!trimmed) return null;
|
|
83
|
+
const parts = trimmed.split(/\s+/);
|
|
84
|
+
if (parts.length !== CRON_FIELDS) return null;
|
|
85
|
+
const minute = parseCronPart(parts[0], CRON_RANGES[0]);
|
|
86
|
+
const hour = parseCronPart(parts[1], CRON_RANGES[1]);
|
|
87
|
+
const dayOfMonth = parseCronPart(parts[2], CRON_RANGES[2]);
|
|
88
|
+
const month = parseCronPart(parts[3], CRON_RANGES[3]);
|
|
89
|
+
const dayOfWeek = parseCronPart(parts[4], CRON_RANGES[4]);
|
|
90
|
+
if (!minute || !hour || !dayOfMonth || !month || !dayOfWeek) return null;
|
|
91
|
+
return {
|
|
92
|
+
minute,
|
|
93
|
+
hour,
|
|
94
|
+
dayOfMonth,
|
|
95
|
+
month,
|
|
96
|
+
dayOfWeek
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function cronMatchesUTC(schedule, candidateMs) {
|
|
100
|
+
const candidate = new Date(candidateMs);
|
|
101
|
+
return schedule.minute.has(candidate.getUTCMinutes()) && schedule.hour.has(candidate.getUTCHours()) && schedule.dayOfMonth.has(candidate.getUTCDate()) && schedule.month.has(candidate.getUTCMonth() + 1) && schedule.dayOfWeek.has(candidate.getUTCDay());
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get the UTC offset in milliseconds for a given IANA timezone.
|
|
105
|
+
* Returns 0 if the timezone is invalid or not provided.
|
|
106
|
+
*/
|
|
107
|
+
function getTimezoneOffsetMs(timezone, atMs) {
|
|
108
|
+
if (!timezone || timezone === "UTC") return 0;
|
|
109
|
+
try {
|
|
110
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
111
|
+
timeZone: timezone,
|
|
112
|
+
year: "numeric",
|
|
113
|
+
month: "2-digit",
|
|
114
|
+
day: "2-digit",
|
|
115
|
+
hour: "2-digit",
|
|
116
|
+
minute: "2-digit",
|
|
117
|
+
second: "2-digit",
|
|
118
|
+
hour12: false
|
|
119
|
+
}).formatToParts(new Date(atMs));
|
|
120
|
+
const get = (type) => {
|
|
121
|
+
const part = parts.find((p) => p.type === type);
|
|
122
|
+
return part ? Number(part.value) : 0;
|
|
123
|
+
};
|
|
124
|
+
return Date.UTC(get("year"), get("month") - 1, get("day"), get("hour"), get("minute"), get("second")) - atMs;
|
|
125
|
+
} catch {
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function cronMatches(schedule, candidateMs, timezone) {
|
|
130
|
+
if (!timezone || timezone === "UTC") return cronMatchesUTC(schedule, candidateMs);
|
|
131
|
+
return cronMatchesUTC(schedule, candidateMs + getTimezoneOffsetMs(timezone, candidateMs));
|
|
132
|
+
}
|
|
133
|
+
function computeNextCronRunAtMs(expression, fromMs, timezone) {
|
|
134
|
+
const schedule = parseCronExpression(expression);
|
|
135
|
+
if (!schedule) return null;
|
|
136
|
+
const start = Math.floor(fromMs / CRON_MINUTE_MS) * CRON_MINUTE_MS;
|
|
137
|
+
const cutoff = start + CRON_SCAN_WINDOW_MS;
|
|
138
|
+
for (let candidate = start + CRON_MINUTE_MS; candidate <= cutoff; candidate += CRON_MINUTE_MS) if (cronMatches(schedule, candidate, timezone)) return candidate;
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
function parseScheduledAtIso(scheduledAtIso) {
|
|
142
|
+
const parsed = Date.parse(scheduledAtIso);
|
|
143
|
+
if (!Number.isFinite(parsed)) return null;
|
|
144
|
+
return parsed;
|
|
145
|
+
}
|
|
146
|
+
function normalizeTriggerIntervalMs(intervalMs) {
|
|
147
|
+
if (!Number.isFinite(intervalMs)) return MIN_TRIGGER_INTERVAL_MS;
|
|
148
|
+
return clamp(Math.floor(intervalMs), MIN_TRIGGER_INTERVAL_MS, MAX_TRIGGER_INTERVAL_MS);
|
|
149
|
+
}
|
|
150
|
+
function resolveTriggerTiming(trigger, nowMs) {
|
|
151
|
+
if (!trigger.enabled) return null;
|
|
152
|
+
if (trigger.triggerType === "interval") {
|
|
153
|
+
const intervalMs = normalizeTriggerIntervalMs(trigger.intervalMs ?? 0);
|
|
154
|
+
return {
|
|
155
|
+
updatedAt: nowMs,
|
|
156
|
+
updateIntervalMs: intervalMs,
|
|
157
|
+
nextRunAtMs: nowMs + intervalMs
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (trigger.triggerType === "once") {
|
|
161
|
+
const scheduledAt = trigger.scheduledAtIso ? parseScheduledAtIso(trigger.scheduledAtIso) : null;
|
|
162
|
+
if (scheduledAt === null) return null;
|
|
163
|
+
const nextRunAtMs = Math.max(nowMs, scheduledAt);
|
|
164
|
+
return {
|
|
165
|
+
updatedAt: nowMs,
|
|
166
|
+
updateIntervalMs: Math.max(0, nextRunAtMs - nowMs),
|
|
167
|
+
nextRunAtMs
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const nextRunAtMs = trigger.cronExpression ? computeNextCronRunAtMs(trigger.cronExpression, nowMs, trigger.timezone) : null;
|
|
171
|
+
if (nextRunAtMs === null) return null;
|
|
172
|
+
return {
|
|
173
|
+
updatedAt: nowMs,
|
|
174
|
+
updateIntervalMs: Math.max(0, nextRunAtMs - nowMs),
|
|
175
|
+
nextRunAtMs
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function buildTriggerMetadata(params) {
|
|
179
|
+
const timing = resolveTriggerTiming(params.trigger, params.nowMs);
|
|
180
|
+
if (!timing) return null;
|
|
181
|
+
return {
|
|
182
|
+
...params.existingMetadata ?? {},
|
|
183
|
+
blocking: true,
|
|
184
|
+
updatedAt: timing.updatedAt,
|
|
185
|
+
updateInterval: timing.updateIntervalMs,
|
|
186
|
+
trigger: {
|
|
187
|
+
...params.trigger,
|
|
188
|
+
nextRunAtMs: timing.nextRunAtMs
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function buildTriggerDedupeKey(parts) {
|
|
193
|
+
const normalized = [
|
|
194
|
+
parts.triggerType,
|
|
195
|
+
normalizeText(parts.instructions).toLowerCase(),
|
|
196
|
+
String(parts.intervalMs ?? ""),
|
|
197
|
+
parts.scheduledAtIso ?? "",
|
|
198
|
+
parts.cronExpression ?? "",
|
|
199
|
+
parts.wakeMode
|
|
200
|
+
].join("|");
|
|
201
|
+
let hash = 5381;
|
|
202
|
+
for (const char of normalized) hash = hash * 33 ^ char.charCodeAt(0);
|
|
203
|
+
return `trigger-${Math.abs(hash >>> 0).toString(16)}`;
|
|
204
|
+
}
|
|
205
|
+
function buildTriggerConfig(params) {
|
|
206
|
+
const previous = params.previous;
|
|
207
|
+
return {
|
|
208
|
+
version: TRIGGER_SCHEMA_VERSION,
|
|
209
|
+
triggerId: params.triggerId,
|
|
210
|
+
displayName: params.draft.displayName,
|
|
211
|
+
instructions: params.draft.instructions,
|
|
212
|
+
triggerType: params.draft.triggerType,
|
|
213
|
+
enabled: params.draft.enabled,
|
|
214
|
+
wakeMode: params.draft.wakeMode,
|
|
215
|
+
createdBy: params.draft.createdBy,
|
|
216
|
+
timezone: params.draft.timezone,
|
|
217
|
+
intervalMs: params.draft.triggerType === "interval" ? normalizeTriggerIntervalMs(params.draft.intervalMs ?? 0) : void 0,
|
|
218
|
+
scheduledAtIso: params.draft.triggerType === "once" ? params.draft.scheduledAtIso : void 0,
|
|
219
|
+
cronExpression: params.draft.triggerType === "cron" ? params.draft.cronExpression : void 0,
|
|
220
|
+
maxRuns: params.draft.maxRuns,
|
|
221
|
+
runCount: previous?.runCount ?? 0,
|
|
222
|
+
dedupeKey: buildTriggerDedupeKey({
|
|
223
|
+
triggerType: params.draft.triggerType,
|
|
224
|
+
instructions: params.draft.instructions,
|
|
225
|
+
intervalMs: params.draft.intervalMs,
|
|
226
|
+
scheduledAtIso: params.draft.scheduledAtIso,
|
|
227
|
+
cronExpression: params.draft.cronExpression,
|
|
228
|
+
wakeMode: params.draft.wakeMode
|
|
229
|
+
}),
|
|
230
|
+
nextRunAtMs: previous?.nextRunAtMs,
|
|
231
|
+
lastRunAtIso: previous?.lastRunAtIso,
|
|
232
|
+
lastStatus: previous?.lastStatus,
|
|
233
|
+
lastError: previous?.lastError
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function normalizeTriggerDraft(params) {
|
|
237
|
+
const displayName = normalizeText(params.input.displayName ?? "") || normalizeText(params.fallback.displayName);
|
|
238
|
+
const instructions = normalizeText(params.input.instructions ?? "") || normalizeText(params.fallback.instructions);
|
|
239
|
+
if (!displayName) return { error: "displayName is required" };
|
|
240
|
+
if (!instructions) return { error: "instructions is required" };
|
|
241
|
+
const triggerType = params.input.triggerType ?? params.fallback.triggerType ?? "interval";
|
|
242
|
+
const wakeMode = params.input.wakeMode ?? params.fallback.wakeMode;
|
|
243
|
+
const enabled = params.input.enabled ?? params.fallback.enabled;
|
|
244
|
+
const createdBy = params.input.createdBy ?? params.fallback.createdBy;
|
|
245
|
+
const timezone = params.input.timezone;
|
|
246
|
+
const intervalMsRaw = typeof params.input.intervalMs === "number" ? params.input.intervalMs : void 0;
|
|
247
|
+
const scheduledAtIso = params.input.scheduledAtIso?.trim();
|
|
248
|
+
const cronExpression = params.input.cronExpression?.trim();
|
|
249
|
+
const maxRuns = typeof params.input.maxRuns === "number" ? Math.floor(params.input.maxRuns) : void 0;
|
|
250
|
+
if (wakeMode !== "inject_now" && wakeMode !== "next_autonomy_cycle") return { error: "wakeMode must be inject_now or next_autonomy_cycle" };
|
|
251
|
+
if (maxRuns !== void 0 && maxRuns <= 0) return { error: "maxRuns must be a positive integer" };
|
|
252
|
+
if (triggerType === "interval") {
|
|
253
|
+
if (intervalMsRaw === void 0) return { error: "intervalMs is required for interval triggers" };
|
|
254
|
+
return { draft: {
|
|
255
|
+
displayName,
|
|
256
|
+
instructions,
|
|
257
|
+
triggerType,
|
|
258
|
+
wakeMode,
|
|
259
|
+
enabled,
|
|
260
|
+
createdBy,
|
|
261
|
+
timezone,
|
|
262
|
+
intervalMs: normalizeTriggerIntervalMs(intervalMsRaw),
|
|
263
|
+
maxRuns
|
|
264
|
+
} };
|
|
265
|
+
}
|
|
266
|
+
if (triggerType === "once") {
|
|
267
|
+
if (!scheduledAtIso || parseScheduledAtIso(scheduledAtIso) === null) return { error: "scheduledAtIso must be a valid ISO timestamp" };
|
|
268
|
+
return { draft: {
|
|
269
|
+
displayName,
|
|
270
|
+
instructions,
|
|
271
|
+
triggerType,
|
|
272
|
+
wakeMode,
|
|
273
|
+
enabled,
|
|
274
|
+
createdBy,
|
|
275
|
+
timezone,
|
|
276
|
+
scheduledAtIso,
|
|
277
|
+
maxRuns
|
|
278
|
+
} };
|
|
279
|
+
}
|
|
280
|
+
if (!cronExpression || !parseCronExpression(cronExpression)) return { error: "cronExpression must be a valid 5-field cron expression" };
|
|
281
|
+
return { draft: {
|
|
282
|
+
displayName,
|
|
283
|
+
instructions,
|
|
284
|
+
triggerType,
|
|
285
|
+
wakeMode,
|
|
286
|
+
enabled,
|
|
287
|
+
createdBy,
|
|
288
|
+
timezone,
|
|
289
|
+
cronExpression,
|
|
290
|
+
maxRuns
|
|
291
|
+
} };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
//#endregion
|
|
295
|
+
export { DISABLED_TRIGGER_INTERVAL_MS, MAX_TRIGGER_RUN_HISTORY, buildTriggerConfig, buildTriggerMetadata, normalizeTriggerDraft };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { miladyMarkdownTheme, tuiTheme } from "../theme.js";
|
|
2
|
+
import { Image, Markdown } from "@mariozechner/pi-tui";
|
|
3
|
+
|
|
4
|
+
//#region src/tui/components/assistant-message.ts
|
|
5
|
+
const imageTheme = { fallbackColor: (s) => tuiTheme.dim(s) };
|
|
6
|
+
/**
|
|
7
|
+
* Assistant message — clean markdown, no prefix.
|
|
8
|
+
* Renders exactly: 1 blank line + markdown content.
|
|
9
|
+
* Thinking traces shown as italic/muted when enabled.
|
|
10
|
+
* Supports inline image attachments.
|
|
11
|
+
*/
|
|
12
|
+
var AssistantMessageComponent = class {
|
|
13
|
+
constructor(showThinking = false, markdownTheme = miladyMarkdownTheme, _agentName) {
|
|
14
|
+
this.showThinking = showThinking;
|
|
15
|
+
this.markdownTheme = markdownTheme;
|
|
16
|
+
this.thinkingMarkdown = null;
|
|
17
|
+
this.images = [];
|
|
18
|
+
this.thinkingText = "";
|
|
19
|
+
this.responseText = "";
|
|
20
|
+
this.isStreaming = true;
|
|
21
|
+
this.markdown = new Markdown("", 1, 0, markdownTheme);
|
|
22
|
+
}
|
|
23
|
+
updateContent(text) {
|
|
24
|
+
this.responseText = text;
|
|
25
|
+
this.rebuildMarkdown();
|
|
26
|
+
}
|
|
27
|
+
updateThinking(text) {
|
|
28
|
+
this.thinkingText = text;
|
|
29
|
+
if (this.showThinking) this.rebuildMarkdown();
|
|
30
|
+
}
|
|
31
|
+
/** Attach an inline image to render below the message text. */
|
|
32
|
+
addImage(attachment) {
|
|
33
|
+
this.images.push(new Image(attachment.base64, attachment.mimeType, imageTheme, {
|
|
34
|
+
maxWidthCells: 60,
|
|
35
|
+
maxHeightCells: 20,
|
|
36
|
+
filename: attachment.filename
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
finalize() {
|
|
40
|
+
this.isStreaming = false;
|
|
41
|
+
this.rebuildMarkdown();
|
|
42
|
+
}
|
|
43
|
+
render(width) {
|
|
44
|
+
const thinkingLines = this.showThinking && this.thinkingMarkdown ? this.thinkingMarkdown.render(width) : [];
|
|
45
|
+
const contentLines = this.markdown.render(width);
|
|
46
|
+
if (thinkingLines.length === 0 && contentLines.length === 0 && this.images.length === 0) return [];
|
|
47
|
+
const lines = [""];
|
|
48
|
+
if (thinkingLines.length > 0) {
|
|
49
|
+
lines.push(...thinkingLines);
|
|
50
|
+
if (contentLines.length > 0) lines.push("");
|
|
51
|
+
}
|
|
52
|
+
lines.push(...contentLines);
|
|
53
|
+
for (const img of this.images) {
|
|
54
|
+
lines.push("");
|
|
55
|
+
lines.push(...img.render(width));
|
|
56
|
+
}
|
|
57
|
+
return lines;
|
|
58
|
+
}
|
|
59
|
+
invalidate() {
|
|
60
|
+
this.markdown.invalidate();
|
|
61
|
+
this.thinkingMarkdown?.invalidate();
|
|
62
|
+
for (const img of this.images) img.invalidate();
|
|
63
|
+
}
|
|
64
|
+
rebuildMarkdown() {
|
|
65
|
+
if (this.showThinking && this.thinkingText.trim()) this.thinkingMarkdown = new Markdown(this.thinkingText.trim(), 1, 0, this.markdownTheme, {
|
|
66
|
+
color: (t) => tuiTheme.muted(t),
|
|
67
|
+
italic: true
|
|
68
|
+
});
|
|
69
|
+
else this.thinkingMarkdown = null;
|
|
70
|
+
const display = this.responseText + (this.isStreaming && this.responseText ? " ▊" : "");
|
|
71
|
+
this.markdown.setText(display.trimEnd());
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
export { AssistantMessageComponent };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Editor, matchesKey } from "@mariozechner/pi-tui";
|
|
2
|
+
|
|
3
|
+
//#region src/tui/components/chat-editor.ts
|
|
4
|
+
/**
|
|
5
|
+
* Small wrapper around @mariozechner/pi-tui's Editor to support Milady-wide keybindings.
|
|
6
|
+
*/
|
|
7
|
+
var ChatEditor = class extends Editor {
|
|
8
|
+
handleInput(data) {
|
|
9
|
+
if (matchesKey(data, "ctrl+c")) {
|
|
10
|
+
this.onCtrlC?.();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (matchesKey(data, "ctrl+p")) {
|
|
14
|
+
this.onCtrlP?.();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (matchesKey(data, "ctrl+e")) {
|
|
18
|
+
this.onCtrlE?.();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (matchesKey(data, "alt+u")) {
|
|
22
|
+
this.onAltU?.();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (matchesKey(data, "ctrl+g")) {
|
|
26
|
+
this.onCtrlG?.();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
super.handleInput(data);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { ChatEditor };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { tuiTheme } from "../theme.js";
|
|
2
|
+
import { ModalFrame } from "./modal-frame.js";
|
|
3
|
+
import { SelectList, getEditorKeybindings } from "@mariozechner/pi-tui";
|
|
4
|
+
|
|
5
|
+
//#region src/tui/components/embeddings-overlay.ts
|
|
6
|
+
/**
|
|
7
|
+
* Embedding preset selector shown as a popup overlay.
|
|
8
|
+
*/
|
|
9
|
+
var EmbeddingsOverlayComponent = class {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.focused = false;
|
|
12
|
+
this.frame = new ModalFrame({
|
|
13
|
+
title: "Embedding models",
|
|
14
|
+
hint: "↑↓ navigate • Enter select • Esc cancel"
|
|
15
|
+
});
|
|
16
|
+
this.selectList = new SelectList(options.options.map((opt) => {
|
|
17
|
+
const status = opt.active ? "active" : opt.downloaded ? "downloaded" : "not downloaded";
|
|
18
|
+
return {
|
|
19
|
+
value: opt.tier,
|
|
20
|
+
label: `${opt.tier.padEnd(12)} ${opt.label}`,
|
|
21
|
+
description: `${opt.dimensions} dims • ${status}`
|
|
22
|
+
};
|
|
23
|
+
}), 8, tuiTheme.selectList);
|
|
24
|
+
const selectedIndex = options.options.findIndex((o) => o.active);
|
|
25
|
+
if (selectedIndex >= 0) this.selectList.setSelectedIndex(selectedIndex);
|
|
26
|
+
this.selectList.onSelect = (item) => {
|
|
27
|
+
options.onSelectTier(item.value);
|
|
28
|
+
};
|
|
29
|
+
this.selectList.onCancel = () => {
|
|
30
|
+
options.onCancel();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
handleInput(data) {
|
|
34
|
+
const kb = getEditorKeybindings();
|
|
35
|
+
if (kb.matches(data, "selectUp") || kb.matches(data, "selectDown") || kb.matches(data, "selectConfirm") || kb.matches(data, "selectCancel")) this.selectList.handleInput(data);
|
|
36
|
+
}
|
|
37
|
+
render(width) {
|
|
38
|
+
return this.frame.render(width, this.selectList.render(width));
|
|
39
|
+
}
|
|
40
|
+
invalidate() {
|
|
41
|
+
this.selectList.invalidate();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { EmbeddingsOverlayComponent };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { visibleWidth } from "@mariozechner/pi-tui";
|
|
3
|
+
|
|
4
|
+
//#region src/tui/components/footer.ts
|
|
5
|
+
var FooterComponent = class {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.hints = [
|
|
8
|
+
{
|
|
9
|
+
key: "Enter",
|
|
10
|
+
label: "send"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
key: "Shift+Enter",
|
|
14
|
+
label: "newline"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: "Ctrl+P",
|
|
18
|
+
label: "model"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: "Ctrl+E",
|
|
22
|
+
label: "expand"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: "Ctrl+C",
|
|
26
|
+
label: "cancel/quit"
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
setHints(hints) {
|
|
31
|
+
this.hints = hints;
|
|
32
|
+
}
|
|
33
|
+
render(width) {
|
|
34
|
+
const parts = this.hints.map((h) => `${chalk.bold(h.key)} ${chalk.dim(h.label)}`);
|
|
35
|
+
const separator = chalk.dim(" │ ");
|
|
36
|
+
const joined = parts.join(separator);
|
|
37
|
+
if (visibleWidth(joined) <= width) return [joined];
|
|
38
|
+
let result = "";
|
|
39
|
+
for (const part of parts) {
|
|
40
|
+
const candidate = result ? result + separator + part : part;
|
|
41
|
+
if (visibleWidth(candidate) > width) break;
|
|
42
|
+
result = candidate;
|
|
43
|
+
}
|
|
44
|
+
if (!result && parts.length > 0) {
|
|
45
|
+
const first = parts[0];
|
|
46
|
+
let truncated = "";
|
|
47
|
+
for (const ch of first) {
|
|
48
|
+
const next = truncated + ch;
|
|
49
|
+
if (visibleWidth(next) > width) break;
|
|
50
|
+
truncated = next;
|
|
51
|
+
}
|
|
52
|
+
return [truncated];
|
|
53
|
+
}
|
|
54
|
+
return [result];
|
|
55
|
+
}
|
|
56
|
+
invalidate() {}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
export { FooterComponent };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AssistantMessageComponent } from "./assistant-message.js";
|
|
2
|
+
import { ChatEditor } from "./chat-editor.js";
|
|
3
|
+
import { renderModalHeader } from "./modal-style.js";
|
|
4
|
+
import { ModalFrame } from "./modal-frame.js";
|
|
5
|
+
import { EmbeddingsOverlayComponent } from "./embeddings-overlay.js";
|
|
6
|
+
import { FooterComponent } from "./footer.js";
|
|
7
|
+
import { ModelSelectorComponent } from "./model-selector.js";
|
|
8
|
+
import { PinnedChatLayout } from "./pinned-chat-layout.js";
|
|
9
|
+
import { PluginsOverlayComponent } from "./plugins-overlay.js";
|
|
10
|
+
import { SettingsOverlayComponent } from "./settings-overlay.js";
|
|
11
|
+
import { StatusBar } from "./status-bar.js";
|
|
12
|
+
import { ToolExecutionComponent } from "./tool-execution.js";
|
|
13
|
+
import { UserMessageComponent } from "./user-message.js";
|
|
14
|
+
|
|
15
|
+
export { };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { tuiTheme } from "../theme.js";
|
|
2
|
+
import { renderModalHeader } from "./modal-style.js";
|
|
3
|
+
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
4
|
+
|
|
5
|
+
//#region src/tui/components/modal-frame.ts
|
|
6
|
+
/**
|
|
7
|
+
* Lightweight frame helper for popup overlays.
|
|
8
|
+
*
|
|
9
|
+
* Keeps header/hints/footer styling consistent while allowing each overlay
|
|
10
|
+
* component to keep its own input/state logic.
|
|
11
|
+
*/
|
|
12
|
+
var ModalFrame = class {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.options = options;
|
|
15
|
+
}
|
|
16
|
+
render(width, bodyLines) {
|
|
17
|
+
const lines = renderModalHeader({
|
|
18
|
+
title: this.options.title,
|
|
19
|
+
hint: this.options.hint
|
|
20
|
+
});
|
|
21
|
+
const indent = this.options.bodyIndent ?? 0;
|
|
22
|
+
const prefix = indent > 0 ? " ".repeat(indent) : "";
|
|
23
|
+
const body = indent > 0 ? bodyLines.map((l) => `${prefix}${l}`) : bodyLines;
|
|
24
|
+
lines.push(...body);
|
|
25
|
+
if (this.options.footerHint) {
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push(tuiTheme.dim(` ${this.options.footerHint}`));
|
|
28
|
+
}
|
|
29
|
+
const contentWidth = Math.max(10, width - 2);
|
|
30
|
+
const top = tuiTheme.dim(`╭${"─".repeat(contentWidth)}╮`);
|
|
31
|
+
const bottom = tuiTheme.dim(`╰${"─".repeat(contentWidth)}╯`);
|
|
32
|
+
return [
|
|
33
|
+
top,
|
|
34
|
+
...lines.map((line) => {
|
|
35
|
+
const clipped = truncateToWidth(line, contentWidth, "");
|
|
36
|
+
const pad = Math.max(0, contentWidth - visibleWidth(clipped));
|
|
37
|
+
return `${tuiTheme.dim("│")}${clipped}${" ".repeat(pad)}${tuiTheme.dim("│")}`;
|
|
38
|
+
}),
|
|
39
|
+
bottom
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { ModalFrame };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { tuiTheme } from "../theme.js";
|
|
2
|
+
|
|
3
|
+
//#region src/tui/components/modal-style.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shared modal header chrome so popup overlays feel consistent.
|
|
6
|
+
*/
|
|
7
|
+
function renderModalHeader(options) {
|
|
8
|
+
const lines = [tuiTheme.bold(` ${options.title}`)];
|
|
9
|
+
if (options.hint) lines.push(tuiTheme.dim(` ${options.hint}`));
|
|
10
|
+
if (options.spacerAfterHeader !== false) lines.push("");
|
|
11
|
+
return lines;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { renderModalHeader };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { tuiTheme } from "../theme.js";
|
|
2
|
+
import { ModalFrame } from "./modal-frame.js";
|
|
3
|
+
import { getEnvApiKey, getModels, getProviders } from "@mariozechner/pi-ai";
|
|
4
|
+
import { Input, SelectList, getEditorKeybindings } from "@mariozechner/pi-tui";
|
|
5
|
+
|
|
6
|
+
//#region src/tui/components/model-selector.ts
|
|
7
|
+
/**
|
|
8
|
+
* Pi-style model selector with a filter input + a scrollable list.
|
|
9
|
+
*/
|
|
10
|
+
var ModelSelectorComponent = class {
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.focused = false;
|
|
13
|
+
this.filterInput = new Input();
|
|
14
|
+
this.modelByKey = /* @__PURE__ */ new Map();
|
|
15
|
+
this.frame = new ModalFrame({
|
|
16
|
+
title: "Select model",
|
|
17
|
+
hint: "type to filter • ↑↓ navigate • Enter select • Esc cancel"
|
|
18
|
+
});
|
|
19
|
+
const items = [];
|
|
20
|
+
for (const provider of getProviders()) for (const model of getModels(provider)) {
|
|
21
|
+
const key = `${model.provider}/${model.id}`;
|
|
22
|
+
this.modelByKey.set(key, model);
|
|
23
|
+
const keyHint = options.hasCredentials?.(model.provider) ?? Boolean(getEnvApiKey(model.provider)) ? "" : " (no key)";
|
|
24
|
+
items.push({
|
|
25
|
+
value: key,
|
|
26
|
+
label: key,
|
|
27
|
+
description: `${model.api}${keyHint}`
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
this.selectList = new SelectList(items, 12, tuiTheme.selectList);
|
|
31
|
+
this.selectList.onSelect = (item) => {
|
|
32
|
+
const model = this.modelByKey.get(item.value);
|
|
33
|
+
if (model) options.onSelect(model);
|
|
34
|
+
};
|
|
35
|
+
this.selectList.onCancel = () => {
|
|
36
|
+
options.onCancel();
|
|
37
|
+
};
|
|
38
|
+
const currentKey = `${options.currentModel.provider}/${options.currentModel.id}`;
|
|
39
|
+
const currentIndex = items.findIndex((i) => i.value === currentKey);
|
|
40
|
+
if (currentIndex >= 0) this.selectList.setSelectedIndex(currentIndex);
|
|
41
|
+
this.filterInput.setValue("");
|
|
42
|
+
}
|
|
43
|
+
handleInput(data) {
|
|
44
|
+
const kb = getEditorKeybindings();
|
|
45
|
+
if (kb.matches(data, "selectUp") || kb.matches(data, "selectDown") || kb.matches(data, "selectConfirm") || kb.matches(data, "selectCancel")) {
|
|
46
|
+
this.selectList.handleInput(data);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const before = this.filterInput.getValue();
|
|
50
|
+
this.filterInput.handleInput(data);
|
|
51
|
+
const after = this.filterInput.getValue();
|
|
52
|
+
if (after !== before) this.selectList.setFilter(after);
|
|
53
|
+
}
|
|
54
|
+
render(width) {
|
|
55
|
+
this.filterInput.focused = this.focused;
|
|
56
|
+
const body = [
|
|
57
|
+
...this.filterInput.render(width).map((l) => ` ${l}`),
|
|
58
|
+
"",
|
|
59
|
+
...this.selectList.render(width)
|
|
60
|
+
];
|
|
61
|
+
return this.frame.render(width, body);
|
|
62
|
+
}
|
|
63
|
+
invalidate() {
|
|
64
|
+
this.filterInput.invalidate();
|
|
65
|
+
this.selectList.invalidate();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { ModelSelectorComponent };
|