orcas-angular 1.0.3 → 1.0.5
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/fesm2022/orcas-angular.mjs +1608 -0
- package/fesm2022/orcas-angular.mjs.map +1 -0
- package/package.json +39 -25
- package/types/orcas-angular.d.ts +460 -0
- package/async/README.md +0 -46
- package/async/async.ts +0 -16
- package/async/cancellation-token.ts +0 -90
- package/dev/README.md +0 -41
- package/dev/console-hook.ts +0 -25
- package/dev/debug.service.ts.example +0 -29
- package/framework/README.md +0 -34
- package/framework/services-init.ts +0 -25
- package/index.ts +0 -25
- package/localization/README.md +0 -73
- package/localization/localization.interface.ts +0 -18
- package/localization/localization.service.ts +0 -131
- package/localization/localize.pipe.ts +0 -30
- package/log/README.md +0 -275
- package/log/echo-provider.ts +0 -27
- package/log/echo.ts +0 -635
- package/log/index.ts +0 -6
- package/log/log-systems.ts +0 -20
- package/navigation/README.md +0 -47
- package/navigation/back-on-click.directive.ts +0 -19
- package/navigation/index.ts +0 -3
- package/navigation/navigation-stack.service.ts +0 -33
- package/storage/README.md +0 -75
- package/storage/capacitor-files.service.ts +0 -38
- package/storage/file-box.service.ts +0 -112
- package/storage/files.ts +0 -42
- package/storage/key-signals.ts +0 -49
- package/storage/local-storage-files.service.ts +0 -49
- package/storage/settings-signals.service.ts +0 -24
- package/storage/settings.service.ts +0 -24
- package/storage/tauri-files.service.ts +0 -69
- package/theme/README.md +0 -44
- package/theme/theme.service.ts +0 -33
- package/ui/README.md +0 -42
- package/ui/context-menu/context-button.component.ts +0 -55
- package/ui/context-menu/context-header.component.ts +0 -15
- package/ui/context-menu/context-menu-trigger.directive.ts +0 -26
- package/ui/context-menu/context-menu.component.ts +0 -95
- package/ui/context-menu/index.ts +0 -4
|
@@ -0,0 +1,1608 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, computed, inject, signal, Pipe, InjectionToken, HostListener, Directive, effect, input, booleanAttribute, output, ViewChild, Component } from '@angular/core';
|
|
3
|
+
import { HttpClient } from '@angular/common/http';
|
|
4
|
+
import * as i1 from '@angular/router';
|
|
5
|
+
import { NavigationEnd } from '@angular/router';
|
|
6
|
+
import * as i2 from '@angular/common';
|
|
7
|
+
import { CommonModule } from '@angular/common';
|
|
8
|
+
import { lastValueFrom } from 'rxjs';
|
|
9
|
+
|
|
10
|
+
class Async {
|
|
11
|
+
static async delay(ms) {
|
|
12
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
13
|
+
}
|
|
14
|
+
static async until(check, timeoutMs = 10000, frequencyMs = 100) {
|
|
15
|
+
let timePassed = 0;
|
|
16
|
+
while (!check() && timePassed < timeoutMs) {
|
|
17
|
+
await Async.delay(frequencyMs);
|
|
18
|
+
timePassed += frequencyMs;
|
|
19
|
+
if (timePassed >= timeoutMs)
|
|
20
|
+
throw new Error('Timeout while waiting for condition');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class CancellationTokenInternal {
|
|
26
|
+
_isCancelled = false;
|
|
27
|
+
constructor(isCancelled = false) {
|
|
28
|
+
this._isCancelled = isCancelled;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Gets whether cancellation has been requested
|
|
32
|
+
*/
|
|
33
|
+
isCancelled() {
|
|
34
|
+
return this._isCancelled;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Throws an error if cancellation has been requested
|
|
38
|
+
*/
|
|
39
|
+
throwIfCancelled() {
|
|
40
|
+
if (this._isCancelled) {
|
|
41
|
+
throw new CancellationError('Operation was cancelled');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Internal method to cancel the token
|
|
46
|
+
*/
|
|
47
|
+
cancel() {
|
|
48
|
+
this._isCancelled = true;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A token that is never cancelled
|
|
52
|
+
*/
|
|
53
|
+
static None = new CancellationTokenInternal(false);
|
|
54
|
+
/**
|
|
55
|
+
* A token that is already cancelled
|
|
56
|
+
*/
|
|
57
|
+
static Cancelled = new CancellationTokenInternal(true);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Error thrown when an operation is cancelled
|
|
61
|
+
*/
|
|
62
|
+
class CancellationError extends Error {
|
|
63
|
+
constructor(message = 'Operation was cancelled') {
|
|
64
|
+
super(message);
|
|
65
|
+
this.name = 'CancellationError';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Source for creating and managing cancellation tokens
|
|
70
|
+
*/
|
|
71
|
+
class CancellationTokenSource {
|
|
72
|
+
_token = new CancellationTokenInternal();
|
|
73
|
+
/**
|
|
74
|
+
* Gets the token currently associated with this source
|
|
75
|
+
*/
|
|
76
|
+
get token() {
|
|
77
|
+
return this._token;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Cancels the current token and creates a new one
|
|
81
|
+
*/
|
|
82
|
+
newUnique(timeoutMs = -1) {
|
|
83
|
+
this._token.cancel();
|
|
84
|
+
this._token = new CancellationTokenInternal();
|
|
85
|
+
if (timeoutMs != -1)
|
|
86
|
+
setTimeout(() => this._token.cancel(), timeoutMs);
|
|
87
|
+
return this._token;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Cancels the current token
|
|
91
|
+
*/
|
|
92
|
+
cancel() {
|
|
93
|
+
this._token.cancel();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
class ConsoleHook {
|
|
98
|
+
static commands = {};
|
|
99
|
+
static initialize() {
|
|
100
|
+
if (!window.r)
|
|
101
|
+
window.r = ConsoleHook.run.bind(ConsoleHook);
|
|
102
|
+
}
|
|
103
|
+
static register(commandName, method) {
|
|
104
|
+
ConsoleHook.initialize();
|
|
105
|
+
ConsoleHook.commands[commandName] = method;
|
|
106
|
+
}
|
|
107
|
+
static run(input, ...additionalParams) {
|
|
108
|
+
const [commandName, ...params] = input.split(' ');
|
|
109
|
+
const command = ConsoleHook.commands[commandName];
|
|
110
|
+
if (command) {
|
|
111
|
+
return command(...params, ...additionalParams);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.error(`Custom command "${commandName}" not found.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class ServicesInit {
|
|
120
|
+
injector;
|
|
121
|
+
constructor(injector) {
|
|
122
|
+
this.injector = injector;
|
|
123
|
+
}
|
|
124
|
+
async init(serviceClass, ...params) {
|
|
125
|
+
let className = `${serviceClass.name || 'unknown'}`;
|
|
126
|
+
const instance = this.injector.get(serviceClass);
|
|
127
|
+
if (!instance)
|
|
128
|
+
throw new Error(`Service not found: ${className}`);
|
|
129
|
+
const hasInit = typeof instance.init === 'function';
|
|
130
|
+
if (params.length > 0 && !hasInit)
|
|
131
|
+
throw new Error(`Service ${className} has no init method but initialization parameters were provided.`);
|
|
132
|
+
if (hasInit)
|
|
133
|
+
await instance.init(...params);
|
|
134
|
+
return instance;
|
|
135
|
+
}
|
|
136
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ServicesInit, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
137
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ServicesInit, providedIn: 'root' });
|
|
138
|
+
}
|
|
139
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ServicesInit, decorators: [{
|
|
140
|
+
type: Injectable,
|
|
141
|
+
args: [{ providedIn: 'root' }]
|
|
142
|
+
}], ctorParameters: () => [{ type: i0.Injector }] });
|
|
143
|
+
|
|
144
|
+
class LocalizationService {
|
|
145
|
+
defaultLanguage = 'en';
|
|
146
|
+
storageKey = 'orcas-language';
|
|
147
|
+
translations = {};
|
|
148
|
+
loaded = false;
|
|
149
|
+
$language;
|
|
150
|
+
$currentLang = computed(() => this.$language(), ...(ngDevMode ? [{ debugName: "$currentLang" }] : []));
|
|
151
|
+
http = inject(HttpClient);
|
|
152
|
+
constructor() {
|
|
153
|
+
this.$language = signal(this.getStoredLanguage(), ...(ngDevMode ? [{ debugName: "$language" }] : []));
|
|
154
|
+
}
|
|
155
|
+
async init(jsonPath = 'assets/translations.json', defaultLanguage = 'en', storageKey = 'orcas-language') {
|
|
156
|
+
this.defaultLanguage = defaultLanguage;
|
|
157
|
+
this.storageKey = storageKey;
|
|
158
|
+
this.$language.set(this.getStoredLanguage());
|
|
159
|
+
try {
|
|
160
|
+
this.translations = await this.http.get(jsonPath).toPromise();
|
|
161
|
+
this.loaded = true;
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
console.error('Failed to load translations:', err);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
getLanguage() {
|
|
168
|
+
return this.$language();
|
|
169
|
+
}
|
|
170
|
+
getDefaultLanguage() {
|
|
171
|
+
return this.defaultLanguage;
|
|
172
|
+
}
|
|
173
|
+
setActiveLanguage(lang) {
|
|
174
|
+
if (lang !== this.$language()) {
|
|
175
|
+
localStorage.setItem(this.storageKey, lang);
|
|
176
|
+
this.$language.set(lang);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
translate(key, params, language) {
|
|
180
|
+
const lang = language || this.getLanguage();
|
|
181
|
+
if (!this.loaded) {
|
|
182
|
+
console.error('Localization: Translations not loaded yet!');
|
|
183
|
+
return key;
|
|
184
|
+
}
|
|
185
|
+
let translation = null;
|
|
186
|
+
// Handle pluralization: try singular suffix __1 first if count is 1
|
|
187
|
+
if (params && params.count === 1)
|
|
188
|
+
translation = this.resolveKey(`${key}__1`);
|
|
189
|
+
if (!translation)
|
|
190
|
+
translation = this.resolveKey(key);
|
|
191
|
+
if (!translation) {
|
|
192
|
+
console.warn(`Localization: Key not found for "${key}".`);
|
|
193
|
+
return key;
|
|
194
|
+
}
|
|
195
|
+
let translatedText = translation[lang];
|
|
196
|
+
if (!translatedText) {
|
|
197
|
+
console.warn(`Localization: Key "${key}" not found for language "${lang}". Falling back to default language.`);
|
|
198
|
+
translatedText = translation[this.defaultLanguage];
|
|
199
|
+
}
|
|
200
|
+
if (!translatedText) {
|
|
201
|
+
console.error(`Localization: Key "${key}" not found for default language "${this.defaultLanguage}".`);
|
|
202
|
+
return key;
|
|
203
|
+
}
|
|
204
|
+
if (params) {
|
|
205
|
+
if (Array.isArray(params))
|
|
206
|
+
return this.replaceArrayParams(translatedText, params);
|
|
207
|
+
else
|
|
208
|
+
return this.replaceObjectParams(translatedText, params);
|
|
209
|
+
}
|
|
210
|
+
return translatedText;
|
|
211
|
+
}
|
|
212
|
+
resolveKey(key) {
|
|
213
|
+
const keys = key.split('.');
|
|
214
|
+
let translation = this.translations;
|
|
215
|
+
for (const k of keys) {
|
|
216
|
+
if (!translation[k])
|
|
217
|
+
return null;
|
|
218
|
+
translation = translation[k];
|
|
219
|
+
}
|
|
220
|
+
return translation;
|
|
221
|
+
}
|
|
222
|
+
getStoredLanguage() {
|
|
223
|
+
return localStorage.getItem(this.storageKey) || this.defaultLanguage;
|
|
224
|
+
}
|
|
225
|
+
replaceArrayParams(text, params) {
|
|
226
|
+
let result = text;
|
|
227
|
+
params.forEach((param, index) => {
|
|
228
|
+
result = result.replace(new RegExp(`\\{\\{${index}\\}\\}`, 'g'), param.toString());
|
|
229
|
+
});
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
replaceObjectParams(text, params) {
|
|
233
|
+
let result = text;
|
|
234
|
+
Object.keys(params).forEach(key => {
|
|
235
|
+
result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), params[key].toString());
|
|
236
|
+
});
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LocalizationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
240
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LocalizationService, providedIn: 'root' });
|
|
241
|
+
}
|
|
242
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LocalizationService, decorators: [{
|
|
243
|
+
type: Injectable,
|
|
244
|
+
args: [{
|
|
245
|
+
providedIn: 'root'
|
|
246
|
+
}]
|
|
247
|
+
}], ctorParameters: () => [] });
|
|
248
|
+
|
|
249
|
+
class LocalizePipe {
|
|
250
|
+
localizationService;
|
|
251
|
+
lastLanguage = '';
|
|
252
|
+
lastKey = '';
|
|
253
|
+
lastParams;
|
|
254
|
+
lastResult = '';
|
|
255
|
+
constructor(localizationService) {
|
|
256
|
+
this.localizationService = localizationService;
|
|
257
|
+
}
|
|
258
|
+
transform(key, params) {
|
|
259
|
+
if (this.localizationService.$currentLang() !== this.lastLanguage
|
|
260
|
+
|| key !== this.lastKey
|
|
261
|
+
|| params !== this.lastParams) {
|
|
262
|
+
this.lastLanguage = this.localizationService.$currentLang();
|
|
263
|
+
this.lastKey = key;
|
|
264
|
+
this.lastParams = params;
|
|
265
|
+
this.lastResult = this.localizationService.translate(key, params);
|
|
266
|
+
}
|
|
267
|
+
return this.lastResult;
|
|
268
|
+
}
|
|
269
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LocalizePipe, deps: [{ token: LocalizationService }], target: i0.ɵɵFactoryTarget.Pipe });
|
|
270
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.0", ngImport: i0, type: LocalizePipe, isStandalone: true, name: "localize", pure: false });
|
|
271
|
+
}
|
|
272
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LocalizePipe, decorators: [{
|
|
273
|
+
type: Pipe,
|
|
274
|
+
args: [{
|
|
275
|
+
name: 'localize',
|
|
276
|
+
standalone: true,
|
|
277
|
+
pure: false
|
|
278
|
+
}]
|
|
279
|
+
}], ctorParameters: () => [{ type: LocalizationService }] });
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Echo - A flexible logging library
|
|
283
|
+
* TypeScript port of Echo.cs core functionality (excluding Unity-specific features)
|
|
284
|
+
*/
|
|
285
|
+
// ============================================================================
|
|
286
|
+
// Enums
|
|
287
|
+
// ============================================================================
|
|
288
|
+
var LogLevel;
|
|
289
|
+
(function (LogLevel) {
|
|
290
|
+
LogLevel[LogLevel["None"] = 0] = "None";
|
|
291
|
+
LogLevel[LogLevel["Error"] = 1] = "Error";
|
|
292
|
+
LogLevel[LogLevel["Warn"] = 2] = "Warn";
|
|
293
|
+
LogLevel[LogLevel["Info"] = 3] = "Info";
|
|
294
|
+
LogLevel[LogLevel["Debug"] = 4] = "Debug";
|
|
295
|
+
})(LogLevel || (LogLevel = {}));
|
|
296
|
+
var LogMode;
|
|
297
|
+
(function (LogMode) {
|
|
298
|
+
LogMode[LogMode["Always"] = 0] = "Always";
|
|
299
|
+
LogMode[LogMode["Once"] = 1] = "Once";
|
|
300
|
+
})(LogMode || (LogMode = {}));
|
|
301
|
+
var SystemColor;
|
|
302
|
+
(function (SystemColor) {
|
|
303
|
+
SystemColor[SystemColor["None"] = 0] = "None";
|
|
304
|
+
SystemColor[SystemColor["LabelOnly"] = 1] = "LabelOnly";
|
|
305
|
+
SystemColor[SystemColor["LabelAndMessage"] = 2] = "LabelAndMessage";
|
|
306
|
+
})(SystemColor || (SystemColor = {}));
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// Configuration
|
|
309
|
+
// ============================================================================
|
|
310
|
+
class LogWriterConfig {
|
|
311
|
+
timestamp = true;
|
|
312
|
+
levelLabels = true;
|
|
313
|
+
levelColors = true;
|
|
314
|
+
systemColor = SystemColor.LabelOnly;
|
|
315
|
+
}
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// Helpers
|
|
318
|
+
// ============================================================================
|
|
319
|
+
class Helpers {
|
|
320
|
+
static fnv1a32(str, hash = 2166136261) {
|
|
321
|
+
for (let i = 0; i < str.length; i++) {
|
|
322
|
+
hash ^= str.charCodeAt(i);
|
|
323
|
+
hash = Math.imul(hash, 16777619);
|
|
324
|
+
}
|
|
325
|
+
return hash >>> 0; // Convert to unsigned 32-bit integer
|
|
326
|
+
}
|
|
327
|
+
static getElementFromHash(collection, stringToHash) {
|
|
328
|
+
const hash = Helpers.fnv1a32(stringToHash);
|
|
329
|
+
const index = hash % collection.length;
|
|
330
|
+
return collection[index];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
class WritersHelpers {
|
|
334
|
+
static getLevelLabel(level) {
|
|
335
|
+
switch (level) {
|
|
336
|
+
case LogLevel.Debug: return "DEBUG";
|
|
337
|
+
case LogLevel.Info: return "INFO";
|
|
338
|
+
case LogLevel.Warn: return "WARN";
|
|
339
|
+
case LogLevel.Error: return "ERROR";
|
|
340
|
+
default: return "???";
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
static systemColors = [
|
|
344
|
+
"\x1b[31m", // Red
|
|
345
|
+
"\x1b[32m", // Green
|
|
346
|
+
"\x1b[34m", // Blue
|
|
347
|
+
"\x1b[33m", // Yellow
|
|
348
|
+
"\x1b[36m", // Cyan
|
|
349
|
+
"\x1b[35m" // Magenta
|
|
350
|
+
];
|
|
351
|
+
static levelColors = new Map([
|
|
352
|
+
[LogLevel.Debug, "\x1b[37m"], // White
|
|
353
|
+
[LogLevel.Info, "\x1b[36m"], // Cyan
|
|
354
|
+
[LogLevel.Warn, "\x1b[33m"], // Yellow
|
|
355
|
+
[LogLevel.Error, "\x1b[31m"] // Red
|
|
356
|
+
]);
|
|
357
|
+
static resetColor = "\x1b[0m";
|
|
358
|
+
static grayColor = "\x1b[90m";
|
|
359
|
+
}
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// Internal Classes
|
|
362
|
+
// ============================================================================
|
|
363
|
+
class HashesManager {
|
|
364
|
+
hashes = new Set();
|
|
365
|
+
tryAdd(system, message) {
|
|
366
|
+
let hash = Helpers.fnv1a32(message);
|
|
367
|
+
hash = Helpers.fnv1a32(system, hash);
|
|
368
|
+
if (this.hashes.has(hash)) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
this.hashes.add(hash);
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
clear() {
|
|
375
|
+
this.hashes.clear();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
class LoggerCore {
|
|
379
|
+
logWriter;
|
|
380
|
+
echoSettings;
|
|
381
|
+
hashes;
|
|
382
|
+
constructor(config, hashes, logger) {
|
|
383
|
+
if (!config)
|
|
384
|
+
throw new Error("config cannot be null");
|
|
385
|
+
if (!hashes)
|
|
386
|
+
throw new Error("hashes cannot be null");
|
|
387
|
+
if (!logger)
|
|
388
|
+
throw new Error("logger cannot be null");
|
|
389
|
+
this.echoSettings = config;
|
|
390
|
+
this.hashes = hashes;
|
|
391
|
+
this.logWriter = logger;
|
|
392
|
+
}
|
|
393
|
+
isEnabled(system, level) {
|
|
394
|
+
return level <= this.echoSettings.getSystemLevel(system);
|
|
395
|
+
}
|
|
396
|
+
shouldLogOnce(system, message) {
|
|
397
|
+
return this.hashes.tryAdd(system, message);
|
|
398
|
+
}
|
|
399
|
+
clearHashes() {
|
|
400
|
+
this.hashes.clear();
|
|
401
|
+
}
|
|
402
|
+
write(level, mode, system, message) {
|
|
403
|
+
if (mode === LogMode.Always || this.shouldLogOnce(system, message)) {
|
|
404
|
+
this.logWriter.writeLog(level, system, message);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
writeIfEnabled(level, mode, system, message) {
|
|
408
|
+
if (this.isEnabled(system, level)) {
|
|
409
|
+
this.write(level, mode, system, message);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
writeIfEnabled1(level, mode, system, format, param1) {
|
|
413
|
+
if (this.isEnabled(system, level)) {
|
|
414
|
+
const message = this.formatString1(format, param1);
|
|
415
|
+
this.write(level, mode, system, message);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
writeIfEnabled2(level, mode, system, format, param1, param2) {
|
|
419
|
+
if (this.isEnabled(system, level)) {
|
|
420
|
+
const message = this.formatString2(format, param1, param2);
|
|
421
|
+
this.write(level, mode, system, message);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
writeIfEnabled3(level, mode, system, format, param1, param2, param3) {
|
|
425
|
+
if (this.isEnabled(system, level)) {
|
|
426
|
+
const message = this.formatString3(format, param1, param2, param3);
|
|
427
|
+
this.write(level, mode, system, message);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
writeIfEnabled4(level, mode, system, format, param1, param2, param3, param4) {
|
|
431
|
+
if (this.isEnabled(system, level)) {
|
|
432
|
+
const message = this.formatString4(format, param1, param2, param3, param4);
|
|
433
|
+
this.write(level, mode, system, message);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
formatString1(format, param1) {
|
|
437
|
+
return format.replace(/\{0\}/g, String(param1));
|
|
438
|
+
}
|
|
439
|
+
formatString2(format, param1, param2) {
|
|
440
|
+
return format.replace(/\{0\}/g, String(param1)).replace(/\{1\}/g, String(param2));
|
|
441
|
+
}
|
|
442
|
+
formatString3(format, param1, param2, param3) {
|
|
443
|
+
return format.replace(/\{0\}/g, String(param1)).replace(/\{1\}/g, String(param2)).replace(/\{2\}/g, String(param3));
|
|
444
|
+
}
|
|
445
|
+
formatString4(format, param1, param2, param3, param4) {
|
|
446
|
+
return format.replace(/\{0\}/g, String(param1)).replace(/\{1\}/g, String(param2)).replace(/\{2\}/g, String(param3)).replace(/\{3\}/g, String(param4));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// ============================================================================
|
|
450
|
+
// Public Classes
|
|
451
|
+
// ============================================================================
|
|
452
|
+
class EchoSettings {
|
|
453
|
+
systemLevels = new Map();
|
|
454
|
+
_defaultLevel = LogLevel.Warn;
|
|
455
|
+
updateCallbacks = [];
|
|
456
|
+
get defaultLevel() {
|
|
457
|
+
return this._defaultLevel;
|
|
458
|
+
}
|
|
459
|
+
onUpdated(callback) {
|
|
460
|
+
this.updateCallbacks.push(callback);
|
|
461
|
+
}
|
|
462
|
+
triggerUpdate() {
|
|
463
|
+
for (const callback of this.updateCallbacks) {
|
|
464
|
+
callback();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
setSystemLevel(system, level) {
|
|
468
|
+
this.throwIfInvalidSystem(system);
|
|
469
|
+
this.systemLevels.set(system, level);
|
|
470
|
+
this.triggerUpdate();
|
|
471
|
+
}
|
|
472
|
+
clearSystemLevel(system) {
|
|
473
|
+
this.throwIfInvalidSystem(system);
|
|
474
|
+
if (this.systemLevels.delete(system)) {
|
|
475
|
+
this.triggerUpdate();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
getSystemLevel(system) {
|
|
479
|
+
this.throwIfInvalidSystem(system);
|
|
480
|
+
return this.systemLevels.get(system) ?? this._defaultLevel;
|
|
481
|
+
}
|
|
482
|
+
tryGetSystemLevel(system) {
|
|
483
|
+
this.throwIfInvalidSystem(system);
|
|
484
|
+
const level = this.systemLevels.get(system);
|
|
485
|
+
return level !== undefined ? { success: true, level } : { success: false };
|
|
486
|
+
}
|
|
487
|
+
clearSystemLevels() {
|
|
488
|
+
this.systemLevels.clear();
|
|
489
|
+
this.triggerUpdate();
|
|
490
|
+
}
|
|
491
|
+
setDefaultLevel(level) {
|
|
492
|
+
this._defaultLevel = level;
|
|
493
|
+
this.triggerUpdate();
|
|
494
|
+
}
|
|
495
|
+
getAllSystemLevels() {
|
|
496
|
+
return this.systemLevels;
|
|
497
|
+
}
|
|
498
|
+
throwIfInvalidSystem(system) {
|
|
499
|
+
if (!system || system.length === 0) {
|
|
500
|
+
throw new Error("System name cannot be null or empty.");
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
class EchoLogger {
|
|
505
|
+
loggerCore;
|
|
506
|
+
constructor(loggerCore) {
|
|
507
|
+
if (!loggerCore)
|
|
508
|
+
throw new Error("loggerCore cannot be null");
|
|
509
|
+
this.loggerCore = loggerCore;
|
|
510
|
+
}
|
|
511
|
+
// Debug Methods
|
|
512
|
+
debug(system, formatOrMessage, param1, param2, param3, param4) {
|
|
513
|
+
if (param1 === undefined) {
|
|
514
|
+
this.loggerCore.writeIfEnabled(LogLevel.Debug, LogMode.Always, system, formatOrMessage);
|
|
515
|
+
}
|
|
516
|
+
else if (param2 === undefined) {
|
|
517
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Debug, LogMode.Always, system, formatOrMessage, param1);
|
|
518
|
+
}
|
|
519
|
+
else if (param3 === undefined) {
|
|
520
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Debug, LogMode.Always, system, formatOrMessage, param1, param2);
|
|
521
|
+
}
|
|
522
|
+
else if (param4 === undefined) {
|
|
523
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Debug, LogMode.Always, system, formatOrMessage, param1, param2, param3);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Debug, LogMode.Always, system, formatOrMessage, param1, param2, param3, param4);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
debug1(system, formatOrMessage, param1, param2, param3, param4) {
|
|
530
|
+
if (param1 === undefined) {
|
|
531
|
+
this.loggerCore.writeIfEnabled(LogLevel.Debug, LogMode.Once, system, formatOrMessage);
|
|
532
|
+
}
|
|
533
|
+
else if (param2 === undefined) {
|
|
534
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Debug, LogMode.Once, system, formatOrMessage, param1);
|
|
535
|
+
}
|
|
536
|
+
else if (param3 === undefined) {
|
|
537
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Debug, LogMode.Once, system, formatOrMessage, param1, param2);
|
|
538
|
+
}
|
|
539
|
+
else if (param4 === undefined) {
|
|
540
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Debug, LogMode.Once, system, formatOrMessage, param1, param2, param3);
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Debug, LogMode.Once, system, formatOrMessage, param1, param2, param3, param4);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
// Info Methods
|
|
547
|
+
info(system, formatOrMessage, param1, param2, param3, param4) {
|
|
548
|
+
if (param1 === undefined) {
|
|
549
|
+
this.loggerCore.writeIfEnabled(LogLevel.Info, LogMode.Always, system, formatOrMessage);
|
|
550
|
+
}
|
|
551
|
+
else if (param2 === undefined) {
|
|
552
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Info, LogMode.Always, system, formatOrMessage, param1);
|
|
553
|
+
}
|
|
554
|
+
else if (param3 === undefined) {
|
|
555
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Info, LogMode.Always, system, formatOrMessage, param1, param2);
|
|
556
|
+
}
|
|
557
|
+
else if (param4 === undefined) {
|
|
558
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Info, LogMode.Always, system, formatOrMessage, param1, param2, param3);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Info, LogMode.Always, system, formatOrMessage, param1, param2, param3, param4);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
info1(system, formatOrMessage, param1, param2, param3, param4) {
|
|
565
|
+
if (param1 === undefined) {
|
|
566
|
+
this.loggerCore.writeIfEnabled(LogLevel.Info, LogMode.Once, system, formatOrMessage);
|
|
567
|
+
}
|
|
568
|
+
else if (param2 === undefined) {
|
|
569
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Info, LogMode.Once, system, formatOrMessage, param1);
|
|
570
|
+
}
|
|
571
|
+
else if (param3 === undefined) {
|
|
572
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Info, LogMode.Once, system, formatOrMessage, param1, param2);
|
|
573
|
+
}
|
|
574
|
+
else if (param4 === undefined) {
|
|
575
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Info, LogMode.Once, system, formatOrMessage, param1, param2, param3);
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Info, LogMode.Once, system, formatOrMessage, param1, param2, param3, param4);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// Warn Methods
|
|
582
|
+
warn(system, formatOrMessage, param1, param2, param3, param4) {
|
|
583
|
+
if (param1 === undefined) {
|
|
584
|
+
this.loggerCore.writeIfEnabled(LogLevel.Warn, LogMode.Always, system, formatOrMessage);
|
|
585
|
+
}
|
|
586
|
+
else if (param2 === undefined) {
|
|
587
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Warn, LogMode.Always, system, formatOrMessage, param1);
|
|
588
|
+
}
|
|
589
|
+
else if (param3 === undefined) {
|
|
590
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Warn, LogMode.Always, system, formatOrMessage, param1, param2);
|
|
591
|
+
}
|
|
592
|
+
else if (param4 === undefined) {
|
|
593
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Warn, LogMode.Always, system, formatOrMessage, param1, param2, param3);
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Warn, LogMode.Always, system, formatOrMessage, param1, param2, param3, param4);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
warn1(system, formatOrMessage, param1, param2, param3, param4) {
|
|
600
|
+
if (param1 === undefined) {
|
|
601
|
+
this.loggerCore.writeIfEnabled(LogLevel.Warn, LogMode.Once, system, formatOrMessage);
|
|
602
|
+
}
|
|
603
|
+
else if (param2 === undefined) {
|
|
604
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Warn, LogMode.Once, system, formatOrMessage, param1);
|
|
605
|
+
}
|
|
606
|
+
else if (param3 === undefined) {
|
|
607
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Warn, LogMode.Once, system, formatOrMessage, param1, param2);
|
|
608
|
+
}
|
|
609
|
+
else if (param4 === undefined) {
|
|
610
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Warn, LogMode.Once, system, formatOrMessage, param1, param2, param3);
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Warn, LogMode.Once, system, formatOrMessage, param1, param2, param3, param4);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
// Error Methods
|
|
617
|
+
error(system, formatOrMessage, param1, param2, param3, param4) {
|
|
618
|
+
if (param1 === undefined) {
|
|
619
|
+
this.loggerCore.writeIfEnabled(LogLevel.Error, LogMode.Always, system, formatOrMessage);
|
|
620
|
+
}
|
|
621
|
+
else if (param2 === undefined) {
|
|
622
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Error, LogMode.Always, system, formatOrMessage, param1);
|
|
623
|
+
}
|
|
624
|
+
else if (param3 === undefined) {
|
|
625
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Error, LogMode.Always, system, formatOrMessage, param1, param2);
|
|
626
|
+
}
|
|
627
|
+
else if (param4 === undefined) {
|
|
628
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Error, LogMode.Always, system, formatOrMessage, param1, param2, param3);
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Error, LogMode.Always, system, formatOrMessage, param1, param2, param3, param4);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
error1(system, formatOrMessage, param1, param2, param3, param4) {
|
|
635
|
+
if (param1 === undefined) {
|
|
636
|
+
this.loggerCore.writeIfEnabled(LogLevel.Error, LogMode.Once, system, formatOrMessage);
|
|
637
|
+
}
|
|
638
|
+
else if (param2 === undefined) {
|
|
639
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Error, LogMode.Once, system, formatOrMessage, param1);
|
|
640
|
+
}
|
|
641
|
+
else if (param3 === undefined) {
|
|
642
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Error, LogMode.Once, system, formatOrMessage, param1, param2);
|
|
643
|
+
}
|
|
644
|
+
else if (param4 === undefined) {
|
|
645
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Error, LogMode.Once, system, formatOrMessage, param1, param2, param3);
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Error, LogMode.Once, system, formatOrMessage, param1, param2, param3, param4);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
class EchoSystemLogger {
|
|
653
|
+
loggerCore;
|
|
654
|
+
system;
|
|
655
|
+
constructor(loggerCore, system) {
|
|
656
|
+
if (!loggerCore)
|
|
657
|
+
throw new Error("loggerCore cannot be null");
|
|
658
|
+
if (!system)
|
|
659
|
+
throw new Error("system cannot be null");
|
|
660
|
+
this.loggerCore = loggerCore;
|
|
661
|
+
this.system = system;
|
|
662
|
+
}
|
|
663
|
+
// Debug Methods
|
|
664
|
+
debug(formatOrMessage, param1, param2, param3, param4) {
|
|
665
|
+
if (param1 === undefined) {
|
|
666
|
+
this.loggerCore.writeIfEnabled(LogLevel.Debug, LogMode.Always, this.system, formatOrMessage);
|
|
667
|
+
}
|
|
668
|
+
else if (param2 === undefined) {
|
|
669
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Debug, LogMode.Always, this.system, formatOrMessage, param1);
|
|
670
|
+
}
|
|
671
|
+
else if (param3 === undefined) {
|
|
672
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Debug, LogMode.Always, this.system, formatOrMessage, param1, param2);
|
|
673
|
+
}
|
|
674
|
+
else if (param4 === undefined) {
|
|
675
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Debug, LogMode.Always, this.system, formatOrMessage, param1, param2, param3);
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Debug, LogMode.Always, this.system, formatOrMessage, param1, param2, param3, param4);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
debug1(formatOrMessage, param1, param2, param3, param4) {
|
|
682
|
+
if (param1 === undefined) {
|
|
683
|
+
this.loggerCore.writeIfEnabled(LogLevel.Debug, LogMode.Once, this.system, formatOrMessage);
|
|
684
|
+
}
|
|
685
|
+
else if (param2 === undefined) {
|
|
686
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Debug, LogMode.Once, this.system, formatOrMessage, param1);
|
|
687
|
+
}
|
|
688
|
+
else if (param3 === undefined) {
|
|
689
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Debug, LogMode.Once, this.system, formatOrMessage, param1, param2);
|
|
690
|
+
}
|
|
691
|
+
else if (param4 === undefined) {
|
|
692
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Debug, LogMode.Once, this.system, formatOrMessage, param1, param2, param3);
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Debug, LogMode.Once, this.system, formatOrMessage, param1, param2, param3, param4);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
// Info Methods
|
|
699
|
+
info(formatOrMessage, param1, param2, param3, param4) {
|
|
700
|
+
if (param1 === undefined) {
|
|
701
|
+
this.loggerCore.writeIfEnabled(LogLevel.Info, LogMode.Always, this.system, formatOrMessage);
|
|
702
|
+
}
|
|
703
|
+
else if (param2 === undefined) {
|
|
704
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Info, LogMode.Always, this.system, formatOrMessage, param1);
|
|
705
|
+
}
|
|
706
|
+
else if (param3 === undefined) {
|
|
707
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Info, LogMode.Always, this.system, formatOrMessage, param1, param2);
|
|
708
|
+
}
|
|
709
|
+
else if (param4 === undefined) {
|
|
710
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Info, LogMode.Always, this.system, formatOrMessage, param1, param2, param3);
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Info, LogMode.Always, this.system, formatOrMessage, param1, param2, param3, param4);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
info1(formatOrMessage, param1, param2, param3, param4) {
|
|
717
|
+
if (param1 === undefined) {
|
|
718
|
+
this.loggerCore.writeIfEnabled(LogLevel.Info, LogMode.Once, this.system, formatOrMessage);
|
|
719
|
+
}
|
|
720
|
+
else if (param2 === undefined) {
|
|
721
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Info, LogMode.Once, this.system, formatOrMessage, param1);
|
|
722
|
+
}
|
|
723
|
+
else if (param3 === undefined) {
|
|
724
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Info, LogMode.Once, this.system, formatOrMessage, param1, param2);
|
|
725
|
+
}
|
|
726
|
+
else if (param4 === undefined) {
|
|
727
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Info, LogMode.Once, this.system, formatOrMessage, param1, param2, param3);
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Info, LogMode.Once, this.system, formatOrMessage, param1, param2, param3, param4);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
// Warn Methods
|
|
734
|
+
warn(formatOrMessage, param1, param2, param3, param4) {
|
|
735
|
+
if (param1 === undefined) {
|
|
736
|
+
this.loggerCore.writeIfEnabled(LogLevel.Warn, LogMode.Always, this.system, formatOrMessage);
|
|
737
|
+
}
|
|
738
|
+
else if (param2 === undefined) {
|
|
739
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Warn, LogMode.Always, this.system, formatOrMessage, param1);
|
|
740
|
+
}
|
|
741
|
+
else if (param3 === undefined) {
|
|
742
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Warn, LogMode.Always, this.system, formatOrMessage, param1, param2);
|
|
743
|
+
}
|
|
744
|
+
else if (param4 === undefined) {
|
|
745
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Warn, LogMode.Always, this.system, formatOrMessage, param1, param2, param3);
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Warn, LogMode.Always, this.system, formatOrMessage, param1, param2, param3, param4);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
warn1(formatOrMessage, param1, param2, param3, param4) {
|
|
752
|
+
if (param1 === undefined) {
|
|
753
|
+
this.loggerCore.writeIfEnabled(LogLevel.Warn, LogMode.Once, this.system, formatOrMessage);
|
|
754
|
+
}
|
|
755
|
+
else if (param2 === undefined) {
|
|
756
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Warn, LogMode.Once, this.system, formatOrMessage, param1);
|
|
757
|
+
}
|
|
758
|
+
else if (param3 === undefined) {
|
|
759
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Warn, LogMode.Once, this.system, formatOrMessage, param1, param2);
|
|
760
|
+
}
|
|
761
|
+
else if (param4 === undefined) {
|
|
762
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Warn, LogMode.Once, this.system, formatOrMessage, param1, param2, param3);
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Warn, LogMode.Once, this.system, formatOrMessage, param1, param2, param3, param4);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
// Error Methods
|
|
769
|
+
error(formatOrMessage, param1, param2, param3, param4) {
|
|
770
|
+
if (param1 === undefined) {
|
|
771
|
+
this.loggerCore.writeIfEnabled(LogLevel.Error, LogMode.Always, this.system, formatOrMessage);
|
|
772
|
+
}
|
|
773
|
+
else if (param2 === undefined) {
|
|
774
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Error, LogMode.Always, this.system, formatOrMessage, param1);
|
|
775
|
+
}
|
|
776
|
+
else if (param3 === undefined) {
|
|
777
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Error, LogMode.Always, this.system, formatOrMessage, param1, param2);
|
|
778
|
+
}
|
|
779
|
+
else if (param4 === undefined) {
|
|
780
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Error, LogMode.Always, this.system, formatOrMessage, param1, param2, param3);
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Error, LogMode.Always, this.system, formatOrMessage, param1, param2, param3, param4);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
error1(formatOrMessage, param1, param2, param3, param4) {
|
|
787
|
+
if (param1 === undefined) {
|
|
788
|
+
this.loggerCore.writeIfEnabled(LogLevel.Error, LogMode.Once, this.system, formatOrMessage);
|
|
789
|
+
}
|
|
790
|
+
else if (param2 === undefined) {
|
|
791
|
+
this.loggerCore.writeIfEnabled1(LogLevel.Error, LogMode.Once, this.system, formatOrMessage, param1);
|
|
792
|
+
}
|
|
793
|
+
else if (param3 === undefined) {
|
|
794
|
+
this.loggerCore.writeIfEnabled2(LogLevel.Error, LogMode.Once, this.system, formatOrMessage, param1, param2);
|
|
795
|
+
}
|
|
796
|
+
else if (param4 === undefined) {
|
|
797
|
+
this.loggerCore.writeIfEnabled3(LogLevel.Error, LogMode.Once, this.system, formatOrMessage, param1, param2, param3);
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
this.loggerCore.writeIfEnabled4(LogLevel.Error, LogMode.Once, this.system, formatOrMessage, param1, param2, param3, param4);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
class Echo {
|
|
805
|
+
loggerCore;
|
|
806
|
+
loggers = new Map();
|
|
807
|
+
_settings;
|
|
808
|
+
constructor(writer) {
|
|
809
|
+
if (!writer)
|
|
810
|
+
throw new Error("writer cannot be null");
|
|
811
|
+
const hashes = new HashesManager();
|
|
812
|
+
this._settings = new EchoSettings();
|
|
813
|
+
this.loggerCore = new LoggerCore(this._settings, hashes, writer);
|
|
814
|
+
}
|
|
815
|
+
getLogger() {
|
|
816
|
+
const key = "";
|
|
817
|
+
if (!this.loggers.has(key)) {
|
|
818
|
+
this.loggers.set(key, new EchoLogger(this.loggerCore));
|
|
819
|
+
}
|
|
820
|
+
return this.loggers.get(key);
|
|
821
|
+
}
|
|
822
|
+
getSystemLogger(system) {
|
|
823
|
+
if (!system || system.length === 0) {
|
|
824
|
+
throw new Error("System name cannot be null or empty.");
|
|
825
|
+
}
|
|
826
|
+
if (!this.loggers.has(system)) {
|
|
827
|
+
this.loggers.set(system, new EchoSystemLogger(this.loggerCore, system));
|
|
828
|
+
}
|
|
829
|
+
return this.loggers.get(system);
|
|
830
|
+
}
|
|
831
|
+
get settings() {
|
|
832
|
+
return this._settings;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// ============================================================================
|
|
836
|
+
// Built-in Log Writers
|
|
837
|
+
// ============================================================================
|
|
838
|
+
class ConsoleLogWriter {
|
|
839
|
+
config;
|
|
840
|
+
constructor(config) {
|
|
841
|
+
if (!config)
|
|
842
|
+
throw new Error("config cannot be null");
|
|
843
|
+
this.config = config;
|
|
844
|
+
}
|
|
845
|
+
writeLog(level, system, message) {
|
|
846
|
+
let output = "";
|
|
847
|
+
if (this.config.timestamp) {
|
|
848
|
+
const now = new Date();
|
|
849
|
+
const timestamp = this.formatTimestamp(now);
|
|
850
|
+
output += `${WritersHelpers.grayColor}[${timestamp}]${WritersHelpers.resetColor} `;
|
|
851
|
+
}
|
|
852
|
+
if (this.config.levelLabels) {
|
|
853
|
+
const levelLabel = WritersHelpers.getLevelLabel(level);
|
|
854
|
+
if (this.config.levelColors) {
|
|
855
|
+
const color = WritersHelpers.levelColors.get(level) ?? "";
|
|
856
|
+
output += `${color}[${levelLabel}]${WritersHelpers.resetColor} `;
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
output += `[${levelLabel}] `;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
const systemColor = this.config.systemColor === SystemColor.LabelOnly ||
|
|
863
|
+
this.config.systemColor === SystemColor.LabelAndMessage
|
|
864
|
+
? Helpers.getElementFromHash(WritersHelpers.systemColors, system)
|
|
865
|
+
: "";
|
|
866
|
+
if (systemColor) {
|
|
867
|
+
output += `${systemColor}[${system}]${WritersHelpers.resetColor} `;
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
output += `[${system}] `;
|
|
871
|
+
}
|
|
872
|
+
if (this.config.systemColor === SystemColor.LabelAndMessage && systemColor) {
|
|
873
|
+
output += `${systemColor}${message}${WritersHelpers.resetColor}`;
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
output += message;
|
|
877
|
+
}
|
|
878
|
+
console.log(output);
|
|
879
|
+
}
|
|
880
|
+
formatTimestamp(date) {
|
|
881
|
+
const year = date.getFullYear();
|
|
882
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
883
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
884
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
885
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
886
|
+
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
887
|
+
const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
|
|
888
|
+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
// ============================================================================
|
|
892
|
+
// Public API Helpers
|
|
893
|
+
// ============================================================================
|
|
894
|
+
class EchoConsole {
|
|
895
|
+
static new(config) {
|
|
896
|
+
const writerConfig = config ?? new LogWriterConfig();
|
|
897
|
+
const writer = new ConsoleLogWriter(writerConfig);
|
|
898
|
+
return new Echo(writer);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Constants for Echo logging system names.
|
|
904
|
+
* Using constants ensures consistency and prevents typos.
|
|
905
|
+
*/
|
|
906
|
+
class LogSystems {
|
|
907
|
+
/** System for profiling and performance measurements */
|
|
908
|
+
static PROFILING = 'Profiling';
|
|
909
|
+
/** System for general application logs */
|
|
910
|
+
static GENERAL = 'General';
|
|
911
|
+
/** System for Git-related operations */
|
|
912
|
+
static GIT = 'Git';
|
|
913
|
+
/** System for UI and visual operations */
|
|
914
|
+
static UI = 'UI';
|
|
915
|
+
/** System for repository operations */
|
|
916
|
+
static REPOSITORY = 'Repository';
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Echo provider for Angular dependency injection.
|
|
921
|
+
* Provides a singleton Echo instance with console writer.
|
|
922
|
+
*/
|
|
923
|
+
/**
|
|
924
|
+
* Injection token for Echo logger instance
|
|
925
|
+
*/
|
|
926
|
+
const ECHO = new InjectionToken('Echo Logger Instance');
|
|
927
|
+
/**
|
|
928
|
+
* Factory function to create Echo instance
|
|
929
|
+
*/
|
|
930
|
+
function echoFactory() {
|
|
931
|
+
return EchoConsole.new();
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Provider for Echo logger
|
|
935
|
+
* Use this in your module providers or inject it in services
|
|
936
|
+
*/
|
|
937
|
+
const ECHO_PROVIDER = {
|
|
938
|
+
provide: ECHO,
|
|
939
|
+
useFactory: echoFactory
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
class NavigationStackService {
|
|
943
|
+
router;
|
|
944
|
+
location;
|
|
945
|
+
history = [];
|
|
946
|
+
constructor(router, location) {
|
|
947
|
+
this.router = router;
|
|
948
|
+
this.location = location;
|
|
949
|
+
this.router.events.subscribe((event) => {
|
|
950
|
+
if (event instanceof NavigationEnd)
|
|
951
|
+
this.history.push(event.urlAfterRedirects);
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
goBack() {
|
|
955
|
+
this.history.pop();
|
|
956
|
+
if (this.history.length > 0)
|
|
957
|
+
this.location.back();
|
|
958
|
+
else
|
|
959
|
+
this.router.navigateByUrl("/").then();
|
|
960
|
+
this.history.pop();
|
|
961
|
+
}
|
|
962
|
+
getBack() {
|
|
963
|
+
if (this.history.length > 1)
|
|
964
|
+
return this.history[this.history.length - 2];
|
|
965
|
+
return '';
|
|
966
|
+
}
|
|
967
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NavigationStackService, deps: [{ token: i1.Router }, { token: i2.Location }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
968
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NavigationStackService, providedIn: 'root' });
|
|
969
|
+
}
|
|
970
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NavigationStackService, decorators: [{
|
|
971
|
+
type: Injectable,
|
|
972
|
+
args: [{
|
|
973
|
+
providedIn: 'root'
|
|
974
|
+
}]
|
|
975
|
+
}], ctorParameters: () => [{ type: i1.Router }, { type: i2.Location }] });
|
|
976
|
+
|
|
977
|
+
class BackOnClickDirective {
|
|
978
|
+
navigationStack;
|
|
979
|
+
constructor(navigationStack) {
|
|
980
|
+
this.navigationStack = navigationStack;
|
|
981
|
+
}
|
|
982
|
+
onClick(event) {
|
|
983
|
+
if (event)
|
|
984
|
+
event.preventDefault();
|
|
985
|
+
this.navigationStack.goBack();
|
|
986
|
+
}
|
|
987
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: BackOnClickDirective, deps: [{ token: NavigationStackService }], target: i0.ɵɵFactoryTarget.Directive });
|
|
988
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: BackOnClickDirective, isStandalone: true, selector: "[back-on-click]", host: { listeners: { "click": "onClick($event)" } }, ngImport: i0 });
|
|
989
|
+
}
|
|
990
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: BackOnClickDirective, decorators: [{
|
|
991
|
+
type: Directive,
|
|
992
|
+
args: [{
|
|
993
|
+
selector: '[back-on-click]',
|
|
994
|
+
standalone: true
|
|
995
|
+
}]
|
|
996
|
+
}], ctorParameters: () => [{ type: NavigationStackService }], propDecorators: { onClick: [{
|
|
997
|
+
type: HostListener,
|
|
998
|
+
args: ['click', ['$event']]
|
|
999
|
+
}] } });
|
|
1000
|
+
|
|
1001
|
+
class FilesService {
|
|
1002
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FilesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1003
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FilesService });
|
|
1004
|
+
}
|
|
1005
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FilesService, decorators: [{
|
|
1006
|
+
type: Injectable
|
|
1007
|
+
}] });
|
|
1008
|
+
|
|
1009
|
+
var Status;
|
|
1010
|
+
(function (Status) {
|
|
1011
|
+
Status[Status["NotInitialized"] = 0] = "NotInitialized";
|
|
1012
|
+
Status[Status["Loading"] = 1] = "Loading";
|
|
1013
|
+
Status[Status["Idle"] = 2] = "Idle";
|
|
1014
|
+
Status[Status["Saving"] = 3] = "Saving";
|
|
1015
|
+
})(Status || (Status = {}));
|
|
1016
|
+
class FileBoxService {
|
|
1017
|
+
files = inject(FilesService);
|
|
1018
|
+
status = Status.NotInitialized;
|
|
1019
|
+
path = '';
|
|
1020
|
+
saveEnqueued = false;
|
|
1021
|
+
$dataWritable = signal({}, ...(ngDevMode ? [{ debugName: "$dataWritable" }] : []));
|
|
1022
|
+
$data = computed(() => {
|
|
1023
|
+
if (this.status === Status.NotInitialized)
|
|
1024
|
+
console.error('Service is not initialized.');
|
|
1025
|
+
else if (this.status === Status.Loading)
|
|
1026
|
+
console.error('Service is loading.');
|
|
1027
|
+
return this.$dataWritable();
|
|
1028
|
+
}, ...(ngDevMode ? [{ debugName: "$data" }] : []));
|
|
1029
|
+
async init(path) {
|
|
1030
|
+
if (this.status !== Status.NotInitialized) {
|
|
1031
|
+
console.error('Service is already initialized.');
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
this.path = path;
|
|
1035
|
+
try {
|
|
1036
|
+
this.status = Status.Loading;
|
|
1037
|
+
if (await this.files.hasInStorage(this.path)) {
|
|
1038
|
+
const fileContent = await this.files.readFromStorage(this.path);
|
|
1039
|
+
this.$dataWritable.set(JSON.parse(fileContent));
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
catch (error) {
|
|
1043
|
+
console.error('Failed to load file:', error);
|
|
1044
|
+
this.$dataWritable.set({});
|
|
1045
|
+
}
|
|
1046
|
+
finally {
|
|
1047
|
+
this.status = Status.Idle;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
has(key) {
|
|
1051
|
+
return key in this.$data();
|
|
1052
|
+
}
|
|
1053
|
+
set(key, value) {
|
|
1054
|
+
this.checkType(value);
|
|
1055
|
+
const newData = { ...this.$dataWritable() };
|
|
1056
|
+
newData[key] = value;
|
|
1057
|
+
this.$dataWritable.set(newData);
|
|
1058
|
+
}
|
|
1059
|
+
setAll(data) {
|
|
1060
|
+
this.$dataWritable.set(data);
|
|
1061
|
+
}
|
|
1062
|
+
remove(key) {
|
|
1063
|
+
const newData = { ...this.$dataWritable() };
|
|
1064
|
+
delete newData[key];
|
|
1065
|
+
this.$dataWritable.set(newData);
|
|
1066
|
+
}
|
|
1067
|
+
checkType(value) {
|
|
1068
|
+
if (value instanceof Function)
|
|
1069
|
+
throw new Error('Cannot save functions.');
|
|
1070
|
+
if (value instanceof Promise)
|
|
1071
|
+
throw new Error('Cannot save promises.');
|
|
1072
|
+
}
|
|
1073
|
+
async save() {
|
|
1074
|
+
if (this.saveEnqueued)
|
|
1075
|
+
return;
|
|
1076
|
+
this.saveEnqueued = true;
|
|
1077
|
+
while (this.status === Status.Saving)
|
|
1078
|
+
await Async.delay(100);
|
|
1079
|
+
this.saveEnqueued = false;
|
|
1080
|
+
try {
|
|
1081
|
+
this.status = Status.Saving;
|
|
1082
|
+
const dataString = JSON.stringify(this.$data());
|
|
1083
|
+
await this.files.writeToStorage(this.path, dataString);
|
|
1084
|
+
}
|
|
1085
|
+
catch (error) {
|
|
1086
|
+
console.error('Failed to save file:', error);
|
|
1087
|
+
}
|
|
1088
|
+
finally {
|
|
1089
|
+
this.status = Status.Idle;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FileBoxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1093
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FileBoxService, providedIn: 'root' });
|
|
1094
|
+
}
|
|
1095
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FileBoxService, decorators: [{
|
|
1096
|
+
type: Injectable,
|
|
1097
|
+
args: [{
|
|
1098
|
+
providedIn: 'root'
|
|
1099
|
+
}]
|
|
1100
|
+
}] });
|
|
1101
|
+
|
|
1102
|
+
class KeySignals {
|
|
1103
|
+
SEPARATOR = '|';
|
|
1104
|
+
getCanonicalKey(path) {
|
|
1105
|
+
return path.join(this.SEPARATOR);
|
|
1106
|
+
}
|
|
1107
|
+
getNewSignal(defaultValue, ...path) {
|
|
1108
|
+
return computed(() => this.getValue(defaultValue, ...path));
|
|
1109
|
+
}
|
|
1110
|
+
getValue(defaultValue, ...path) {
|
|
1111
|
+
const key = this.getCanonicalKey(path);
|
|
1112
|
+
const data = this.$data();
|
|
1113
|
+
return key in data ? data[key] : defaultValue;
|
|
1114
|
+
}
|
|
1115
|
+
async set(value, ...path) {
|
|
1116
|
+
const key = this.getCanonicalKey(path);
|
|
1117
|
+
await this.setRawValue(key, value);
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Clears all keys that start with the given prefix.
|
|
1121
|
+
*/
|
|
1122
|
+
async clearByPrefix(...pathPrefix) {
|
|
1123
|
+
const prefix = this.getCanonicalKey(pathPrefix) + this.SEPARATOR;
|
|
1124
|
+
const data = this.$data();
|
|
1125
|
+
const updatedData = { ...data };
|
|
1126
|
+
let changed = false;
|
|
1127
|
+
for (const key in updatedData) {
|
|
1128
|
+
if (key.startsWith(prefix)) {
|
|
1129
|
+
delete updatedData[key];
|
|
1130
|
+
changed = true;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (changed) {
|
|
1134
|
+
await this.setMultipleRawValues(updatedData);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
class SettingsSignalsService extends KeySignals {
|
|
1140
|
+
filebox = inject(FileBoxService);
|
|
1141
|
+
$data() {
|
|
1142
|
+
return this.filebox.$data();
|
|
1143
|
+
}
|
|
1144
|
+
async setRawValue(key, value) {
|
|
1145
|
+
this.filebox.set(key, value);
|
|
1146
|
+
await this.filebox.save();
|
|
1147
|
+
}
|
|
1148
|
+
async setMultipleRawValues(values) {
|
|
1149
|
+
this.filebox.setAll(values);
|
|
1150
|
+
await this.filebox.save();
|
|
1151
|
+
}
|
|
1152
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SettingsSignalsService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1153
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SettingsSignalsService, providedIn: 'root' });
|
|
1154
|
+
}
|
|
1155
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SettingsSignalsService, decorators: [{
|
|
1156
|
+
type: Injectable,
|
|
1157
|
+
args: [{
|
|
1158
|
+
providedIn: 'root'
|
|
1159
|
+
}]
|
|
1160
|
+
}] });
|
|
1161
|
+
|
|
1162
|
+
class SettingsService {
|
|
1163
|
+
fileboxService = inject(FileBoxService);
|
|
1164
|
+
sss = inject(SettingsSignalsService);
|
|
1165
|
+
SETTINGS_KEY = "app-settings";
|
|
1166
|
+
getNewSignal(defaultValue, ...path) {
|
|
1167
|
+
return this.sss.getNewSignal(defaultValue, this.SETTINGS_KEY, ...path);
|
|
1168
|
+
}
|
|
1169
|
+
async set(value, ...path) {
|
|
1170
|
+
await this.sss.set(value, this.SETTINGS_KEY, ...path);
|
|
1171
|
+
}
|
|
1172
|
+
async save() {
|
|
1173
|
+
await this.fileboxService.save();
|
|
1174
|
+
}
|
|
1175
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1176
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SettingsService, providedIn: "root" });
|
|
1177
|
+
}
|
|
1178
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SettingsService, decorators: [{
|
|
1179
|
+
type: Injectable,
|
|
1180
|
+
args: [{
|
|
1181
|
+
providedIn: "root"
|
|
1182
|
+
}]
|
|
1183
|
+
}] });
|
|
1184
|
+
|
|
1185
|
+
class TauriFilesService extends FilesService {
|
|
1186
|
+
static _isTauri = null;
|
|
1187
|
+
static isSupported() {
|
|
1188
|
+
if (this._isTauri !== null)
|
|
1189
|
+
return this._isTauri;
|
|
1190
|
+
try {
|
|
1191
|
+
// Check for window.__TAURI_INTERNALS__ which is usually present in v2
|
|
1192
|
+
this._isTauri = !!(window.__TAURI_INTERNALS__);
|
|
1193
|
+
}
|
|
1194
|
+
catch {
|
|
1195
|
+
this._isTauri = false;
|
|
1196
|
+
}
|
|
1197
|
+
return this._isTauri;
|
|
1198
|
+
}
|
|
1199
|
+
async init() {
|
|
1200
|
+
// No special initialization needed for Tauri FS plugin beyond what is handled lazily
|
|
1201
|
+
}
|
|
1202
|
+
async joinStoragePath(filePath) {
|
|
1203
|
+
const { appLocalDataDir, join } = await import('@tauri-apps/api/path');
|
|
1204
|
+
const dataDir = await appLocalDataDir();
|
|
1205
|
+
return await join(dataDir, filePath);
|
|
1206
|
+
}
|
|
1207
|
+
async hasInStorage(filePath) {
|
|
1208
|
+
try {
|
|
1209
|
+
const { exists } = await import('@tauri-apps/plugin-fs');
|
|
1210
|
+
const { BaseDirectory } = await import('@tauri-apps/api/path');
|
|
1211
|
+
return await exists(filePath, { baseDir: BaseDirectory.AppLocalData });
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
console.error('TauriFilesService.hasInStorage error:', error);
|
|
1215
|
+
return false;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
async readFromStorage(filePath) {
|
|
1219
|
+
const { readTextFile } = await import('@tauri-apps/plugin-fs');
|
|
1220
|
+
const { BaseDirectory } = await import('@tauri-apps/api/path');
|
|
1221
|
+
return await readTextFile(filePath, { baseDir: BaseDirectory.AppLocalData });
|
|
1222
|
+
}
|
|
1223
|
+
async writeToStorage(filePath, data) {
|
|
1224
|
+
const { writeTextFile } = await import('@tauri-apps/plugin-fs');
|
|
1225
|
+
const { BaseDirectory } = await import('@tauri-apps/api/path');
|
|
1226
|
+
await writeTextFile(filePath, data, { baseDir: BaseDirectory.AppLocalData });
|
|
1227
|
+
}
|
|
1228
|
+
async hasInProject(filePath) {
|
|
1229
|
+
try {
|
|
1230
|
+
const response = await fetch(filePath, { method: 'HEAD' });
|
|
1231
|
+
return response.ok;
|
|
1232
|
+
}
|
|
1233
|
+
catch {
|
|
1234
|
+
return false;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
async readFromProject(filePath) {
|
|
1238
|
+
const response = await fetch(filePath);
|
|
1239
|
+
if (!response.ok) {
|
|
1240
|
+
throw new Error(`Failed to read project file: ${filePath} (${response.status})`);
|
|
1241
|
+
}
|
|
1242
|
+
return await response.text();
|
|
1243
|
+
}
|
|
1244
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: TauriFilesService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1245
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: TauriFilesService });
|
|
1246
|
+
}
|
|
1247
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: TauriFilesService, decorators: [{
|
|
1248
|
+
type: Injectable
|
|
1249
|
+
}] });
|
|
1250
|
+
|
|
1251
|
+
class CapacitorFilesService extends FilesService {
|
|
1252
|
+
static isSupported() {
|
|
1253
|
+
// Capacitor is not yet implemented, so returning false
|
|
1254
|
+
return false;
|
|
1255
|
+
}
|
|
1256
|
+
async init() {
|
|
1257
|
+
throw new Error('CapacitorFilesService not implemented');
|
|
1258
|
+
}
|
|
1259
|
+
async joinStoragePath(filePath) {
|
|
1260
|
+
return null;
|
|
1261
|
+
}
|
|
1262
|
+
async hasInStorage(filePath) {
|
|
1263
|
+
throw new Error('Method not implemented.');
|
|
1264
|
+
}
|
|
1265
|
+
async readFromStorage(filePath) {
|
|
1266
|
+
throw new Error('Method not implemented.');
|
|
1267
|
+
}
|
|
1268
|
+
async writeToStorage(filePath, data) {
|
|
1269
|
+
throw new Error('Method not implemented.');
|
|
1270
|
+
}
|
|
1271
|
+
async hasInProject(filePath) {
|
|
1272
|
+
throw new Error('Method not implemented.');
|
|
1273
|
+
}
|
|
1274
|
+
async readFromProject(filePath) {
|
|
1275
|
+
throw new Error('Method not implemented.');
|
|
1276
|
+
}
|
|
1277
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CapacitorFilesService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1278
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CapacitorFilesService });
|
|
1279
|
+
}
|
|
1280
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CapacitorFilesService, decorators: [{
|
|
1281
|
+
type: Injectable
|
|
1282
|
+
}] });
|
|
1283
|
+
|
|
1284
|
+
class LocalStorageFilesService extends FilesService {
|
|
1285
|
+
http = inject(HttpClient);
|
|
1286
|
+
static isSupported() {
|
|
1287
|
+
return typeof window !== 'undefined' && !!window.localStorage;
|
|
1288
|
+
}
|
|
1289
|
+
async init() {
|
|
1290
|
+
// LocalStorage is ready immediately
|
|
1291
|
+
}
|
|
1292
|
+
async joinStoragePath(filePath) {
|
|
1293
|
+
return null;
|
|
1294
|
+
}
|
|
1295
|
+
async hasInStorage(filePath) {
|
|
1296
|
+
return localStorage.getItem(filePath) !== null;
|
|
1297
|
+
}
|
|
1298
|
+
async readFromStorage(filePath) {
|
|
1299
|
+
const data = localStorage.getItem(filePath);
|
|
1300
|
+
if (data === null)
|
|
1301
|
+
throw new Error(`File not found in localStorage: ${filePath}`);
|
|
1302
|
+
return data;
|
|
1303
|
+
}
|
|
1304
|
+
async writeToStorage(filePath, data) {
|
|
1305
|
+
localStorage.setItem(filePath, data);
|
|
1306
|
+
}
|
|
1307
|
+
async hasInProject(filePath) {
|
|
1308
|
+
try {
|
|
1309
|
+
// In a browser, we check if we can fetch it via HTTP
|
|
1310
|
+
await lastValueFrom(this.http.head(filePath));
|
|
1311
|
+
return true;
|
|
1312
|
+
}
|
|
1313
|
+
catch {
|
|
1314
|
+
return false;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
async readFromProject(filePath) {
|
|
1318
|
+
return await lastValueFrom(this.http.get(filePath, { responseType: 'text' }));
|
|
1319
|
+
}
|
|
1320
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LocalStorageFilesService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1321
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LocalStorageFilesService });
|
|
1322
|
+
}
|
|
1323
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LocalStorageFilesService, decorators: [{
|
|
1324
|
+
type: Injectable
|
|
1325
|
+
}] });
|
|
1326
|
+
|
|
1327
|
+
var ThemeType;
|
|
1328
|
+
(function (ThemeType) {
|
|
1329
|
+
ThemeType["Unset"] = "";
|
|
1330
|
+
ThemeType["Light"] = "light";
|
|
1331
|
+
ThemeType["Dark"] = "dark";
|
|
1332
|
+
})(ThemeType || (ThemeType = {}));
|
|
1333
|
+
class ThemeService {
|
|
1334
|
+
settings = inject(SettingsService);
|
|
1335
|
+
$theme = this.settings.getNewSignal(ThemeType.Unset, 'theme');
|
|
1336
|
+
$darkMode = computed(() => {
|
|
1337
|
+
const theme = this.$theme();
|
|
1338
|
+
let isDarkMode = theme === ThemeType.Unset
|
|
1339
|
+
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
1340
|
+
: theme === ThemeType.Dark;
|
|
1341
|
+
return isDarkMode;
|
|
1342
|
+
}, ...(ngDevMode ? [{ debugName: "$darkMode" }] : []));
|
|
1343
|
+
effectSetDarkMode = effect(async () => {
|
|
1344
|
+
document.documentElement.classList.toggle('dark', this.$darkMode());
|
|
1345
|
+
}, ...(ngDevMode ? [{ debugName: "effectSetDarkMode" }] : []));
|
|
1346
|
+
async setTheme(theme) {
|
|
1347
|
+
await this.settings.set(theme, 'theme');
|
|
1348
|
+
}
|
|
1349
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1350
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ThemeService, providedIn: 'root' });
|
|
1351
|
+
}
|
|
1352
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ThemeService, decorators: [{
|
|
1353
|
+
type: Injectable,
|
|
1354
|
+
args: [{
|
|
1355
|
+
providedIn: 'root'
|
|
1356
|
+
}]
|
|
1357
|
+
}] });
|
|
1358
|
+
|
|
1359
|
+
class ContextMenuComponent {
|
|
1360
|
+
elementRef;
|
|
1361
|
+
$isSubmenu = input(false, { ...(ngDevMode ? { debugName: "$isSubmenu" } : {}), transform: booleanAttribute });
|
|
1362
|
+
$isVisible = signal(false, ...(ngDevMode ? [{ debugName: "$isVisible" }] : []));
|
|
1363
|
+
$isMeasuring = signal(false, ...(ngDevMode ? [{ debugName: "$isMeasuring" }] : []));
|
|
1364
|
+
$x = signal(0, ...(ngDevMode ? [{ debugName: "$x" }] : []));
|
|
1365
|
+
$y = signal(0, ...(ngDevMode ? [{ debugName: "$y" }] : []));
|
|
1366
|
+
container;
|
|
1367
|
+
close = output();
|
|
1368
|
+
constructor(elementRef) {
|
|
1369
|
+
this.elementRef = elementRef;
|
|
1370
|
+
}
|
|
1371
|
+
onDocumentClick(event) {
|
|
1372
|
+
if (this.$isSubmenu())
|
|
1373
|
+
return;
|
|
1374
|
+
// If visible and click is outside, close
|
|
1375
|
+
if (this.$isVisible() && !this.elementRef.nativeElement.contains(event.target)) {
|
|
1376
|
+
this.closeMenu();
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
show(x, y) {
|
|
1380
|
+
if (this.$isSubmenu())
|
|
1381
|
+
return;
|
|
1382
|
+
this.$x.set(x);
|
|
1383
|
+
this.$y.set(y);
|
|
1384
|
+
this.$isMeasuring.set(true);
|
|
1385
|
+
this.$isVisible.set(true);
|
|
1386
|
+
// Wait for DOM to render the menu so we can measure it
|
|
1387
|
+
setTimeout(() => {
|
|
1388
|
+
if (this.container) {
|
|
1389
|
+
const rect = this.container.nativeElement.getBoundingClientRect();
|
|
1390
|
+
const width = rect.width;
|
|
1391
|
+
const height = rect.height;
|
|
1392
|
+
const windowWidth = window.innerWidth;
|
|
1393
|
+
const windowHeight = window.innerHeight;
|
|
1394
|
+
let newX = x;
|
|
1395
|
+
let newY = y;
|
|
1396
|
+
if (x + width > windowWidth)
|
|
1397
|
+
newX = x - width;
|
|
1398
|
+
if (y + height > windowHeight)
|
|
1399
|
+
newY = y - height;
|
|
1400
|
+
// Final safety check to ensure it doesn't go off the top/left edges
|
|
1401
|
+
this.$x.set(Math.max(0, newX));
|
|
1402
|
+
this.$y.set(Math.max(0, newY));
|
|
1403
|
+
}
|
|
1404
|
+
this.$isMeasuring.set(false);
|
|
1405
|
+
}, 0);
|
|
1406
|
+
}
|
|
1407
|
+
hide() {
|
|
1408
|
+
this.$isVisible.set(false);
|
|
1409
|
+
this.$isMeasuring.set(false);
|
|
1410
|
+
}
|
|
1411
|
+
closeMenu() {
|
|
1412
|
+
this.hide();
|
|
1413
|
+
this.close.emit();
|
|
1414
|
+
}
|
|
1415
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ContextMenuComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1416
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ContextMenuComponent, isStandalone: true, selector: "context-menu", inputs: { $isSubmenu: { classPropertyName: "$isSubmenu", publicName: "$isSubmenu", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close" }, host: { listeners: { "document:mousedown": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], ngImport: i0, template: `
|
|
1417
|
+
@if ($isVisible() || $isSubmenu()) {
|
|
1418
|
+
<div
|
|
1419
|
+
#container
|
|
1420
|
+
[class.fixed]="!$isSubmenu()"
|
|
1421
|
+
[class.absolute]="$isSubmenu()"
|
|
1422
|
+
[class.left-full]="$isSubmenu()"
|
|
1423
|
+
[class.top-0]="$isSubmenu()"
|
|
1424
|
+
[class.ml-[-2px]]="$isSubmenu()"
|
|
1425
|
+
class="bg-white dark:bg-[#2a2a2a] border border-light-border dark:border-dark-border rounded shadow-lg z-50 min-w-[200px] text-light-text-primary dark:text-dark-text-primary py-1"
|
|
1426
|
+
[style.left.px]="!$isSubmenu() ? $x() : null"
|
|
1427
|
+
[style.top.px]="!$isSubmenu() ? $y() : null"
|
|
1428
|
+
[style.visibility]="$isMeasuring() ? 'hidden' : 'visible'"
|
|
1429
|
+
(click)="$event.stopPropagation()">
|
|
1430
|
+
<ng-content></ng-content>
|
|
1431
|
+
</div>
|
|
1432
|
+
}
|
|
1433
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
1434
|
+
}
|
|
1435
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ContextMenuComponent, decorators: [{
|
|
1436
|
+
type: Component,
|
|
1437
|
+
args: [{
|
|
1438
|
+
selector: 'context-menu',
|
|
1439
|
+
standalone: true,
|
|
1440
|
+
imports: [CommonModule],
|
|
1441
|
+
template: `
|
|
1442
|
+
@if ($isVisible() || $isSubmenu()) {
|
|
1443
|
+
<div
|
|
1444
|
+
#container
|
|
1445
|
+
[class.fixed]="!$isSubmenu()"
|
|
1446
|
+
[class.absolute]="$isSubmenu()"
|
|
1447
|
+
[class.left-full]="$isSubmenu()"
|
|
1448
|
+
[class.top-0]="$isSubmenu()"
|
|
1449
|
+
[class.ml-[-2px]]="$isSubmenu()"
|
|
1450
|
+
class="bg-white dark:bg-[#2a2a2a] border border-light-border dark:border-dark-border rounded shadow-lg z-50 min-w-[200px] text-light-text-primary dark:text-dark-text-primary py-1"
|
|
1451
|
+
[style.left.px]="!$isSubmenu() ? $x() : null"
|
|
1452
|
+
[style.top.px]="!$isSubmenu() ? $y() : null"
|
|
1453
|
+
[style.visibility]="$isMeasuring() ? 'hidden' : 'visible'"
|
|
1454
|
+
(click)="$event.stopPropagation()">
|
|
1455
|
+
<ng-content></ng-content>
|
|
1456
|
+
</div>
|
|
1457
|
+
}
|
|
1458
|
+
`
|
|
1459
|
+
}]
|
|
1460
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { $isSubmenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "$isSubmenu", required: false }] }], container: [{
|
|
1461
|
+
type: ViewChild,
|
|
1462
|
+
args: ['container']
|
|
1463
|
+
}], close: [{ type: i0.Output, args: ["close"] }], onDocumentClick: [{
|
|
1464
|
+
type: HostListener,
|
|
1465
|
+
args: ['document:mousedown', ['$event']]
|
|
1466
|
+
}] } });
|
|
1467
|
+
|
|
1468
|
+
class ContextHeaderComponent {
|
|
1469
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ContextHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1470
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: ContextHeaderComponent, isStandalone: true, selector: "context-header", ngImport: i0, template: `
|
|
1471
|
+
<div
|
|
1472
|
+
class="px-3 py-1.5 text-xs font-semibold text-light-text-secondary dark:text-dark-text-secondary truncate border-b border-light-border dark:border-dark-border mb-1">
|
|
1473
|
+
<ng-content></ng-content>
|
|
1474
|
+
</div>
|
|
1475
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
1476
|
+
}
|
|
1477
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ContextHeaderComponent, decorators: [{
|
|
1478
|
+
type: Component,
|
|
1479
|
+
args: [{
|
|
1480
|
+
selector: 'context-header',
|
|
1481
|
+
standalone: true,
|
|
1482
|
+
imports: [CommonModule],
|
|
1483
|
+
template: `
|
|
1484
|
+
<div
|
|
1485
|
+
class="px-3 py-1.5 text-xs font-semibold text-light-text-secondary dark:text-dark-text-secondary truncate border-b border-light-border dark:border-dark-border mb-1">
|
|
1486
|
+
<ng-content></ng-content>
|
|
1487
|
+
</div>
|
|
1488
|
+
`
|
|
1489
|
+
}]
|
|
1490
|
+
}] });
|
|
1491
|
+
|
|
1492
|
+
class ContextButtonComponent {
|
|
1493
|
+
danger = input(false, { ...(ngDevMode ? { debugName: "danger" } : {}), transform: booleanAttribute });
|
|
1494
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : {}), transform: booleanAttribute });
|
|
1495
|
+
hasSubmenu = input(false, { ...(ngDevMode ? { debugName: "hasSubmenu" } : {}), transform: booleanAttribute });
|
|
1496
|
+
$showSubmenu = signal(false, ...(ngDevMode ? [{ debugName: "$showSubmenu" }] : []));
|
|
1497
|
+
onMouseEnter() {
|
|
1498
|
+
if (this.hasSubmenu() && !this.disabled()) {
|
|
1499
|
+
this.$showSubmenu.set(true);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
onMouseLeave() {
|
|
1503
|
+
if (this.hasSubmenu() && !this.disabled()) {
|
|
1504
|
+
this.$showSubmenu.set(false);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ContextButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1508
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ContextButtonComponent, isStandalone: true, selector: "context-button", inputs: { danger: { classPropertyName: "danger", publicName: "danger", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, hasSubmenu: { classPropertyName: "hasSubmenu", publicName: "hasSubmenu", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
1509
|
+
<div class="relative" (mouseenter)="onMouseEnter()" (mouseleave)="onMouseLeave()">
|
|
1510
|
+
<button
|
|
1511
|
+
class="w-full text-left px-3 py-2 text-sm transition-colors duration-100 flex items-center justify-between gap-2"
|
|
1512
|
+
[class.hover:bg-light-bg-secondary]="!disabled()"
|
|
1513
|
+
[class.dark:hover:bg-[#383838]]="!disabled()"
|
|
1514
|
+
[class.text-red-500]="danger() && !disabled()"
|
|
1515
|
+
[class.hover:bg-red-500]="danger() && !disabled()"
|
|
1516
|
+
[class.hover:text-white]="danger() && !disabled()"
|
|
1517
|
+
[class.opacity-50]="disabled()"
|
|
1518
|
+
[class.cursor-not-allowed]="disabled()"
|
|
1519
|
+
[disabled]="disabled()">
|
|
1520
|
+
<div class="flex items-center gap-2">
|
|
1521
|
+
<ng-content select="[icon]"></ng-content>
|
|
1522
|
+
<ng-content></ng-content>
|
|
1523
|
+
</div>
|
|
1524
|
+
@if (hasSubmenu()) {
|
|
1525
|
+
<span class="text-[10px] text-light-text-secondary opacity-50">▶</span>
|
|
1526
|
+
}
|
|
1527
|
+
</button>
|
|
1528
|
+
|
|
1529
|
+
@if (hasSubmenu() && $showSubmenu() && !disabled()) {
|
|
1530
|
+
<div class="absolute left-full top-0 ml-[-2px] z-[60]">
|
|
1531
|
+
<ng-content select="context-menu"></ng-content>
|
|
1532
|
+
</div>
|
|
1533
|
+
}
|
|
1534
|
+
</div>
|
|
1535
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
1536
|
+
}
|
|
1537
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ContextButtonComponent, decorators: [{
|
|
1538
|
+
type: Component,
|
|
1539
|
+
args: [{
|
|
1540
|
+
selector: 'context-button',
|
|
1541
|
+
standalone: true,
|
|
1542
|
+
imports: [CommonModule],
|
|
1543
|
+
template: `
|
|
1544
|
+
<div class="relative" (mouseenter)="onMouseEnter()" (mouseleave)="onMouseLeave()">
|
|
1545
|
+
<button
|
|
1546
|
+
class="w-full text-left px-3 py-2 text-sm transition-colors duration-100 flex items-center justify-between gap-2"
|
|
1547
|
+
[class.hover:bg-light-bg-secondary]="!disabled()"
|
|
1548
|
+
[class.dark:hover:bg-[#383838]]="!disabled()"
|
|
1549
|
+
[class.text-red-500]="danger() && !disabled()"
|
|
1550
|
+
[class.hover:bg-red-500]="danger() && !disabled()"
|
|
1551
|
+
[class.hover:text-white]="danger() && !disabled()"
|
|
1552
|
+
[class.opacity-50]="disabled()"
|
|
1553
|
+
[class.cursor-not-allowed]="disabled()"
|
|
1554
|
+
[disabled]="disabled()">
|
|
1555
|
+
<div class="flex items-center gap-2">
|
|
1556
|
+
<ng-content select="[icon]"></ng-content>
|
|
1557
|
+
<ng-content></ng-content>
|
|
1558
|
+
</div>
|
|
1559
|
+
@if (hasSubmenu()) {
|
|
1560
|
+
<span class="text-[10px] text-light-text-secondary opacity-50">▶</span>
|
|
1561
|
+
}
|
|
1562
|
+
</button>
|
|
1563
|
+
|
|
1564
|
+
@if (hasSubmenu() && $showSubmenu() && !disabled()) {
|
|
1565
|
+
<div class="absolute left-full top-0 ml-[-2px] z-[60]">
|
|
1566
|
+
<ng-content select="context-menu"></ng-content>
|
|
1567
|
+
</div>
|
|
1568
|
+
}
|
|
1569
|
+
</div>
|
|
1570
|
+
`
|
|
1571
|
+
}]
|
|
1572
|
+
}], propDecorators: { danger: [{ type: i0.Input, args: [{ isSignal: true, alias: "danger", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], hasSubmenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasSubmenu", required: false }] }] } });
|
|
1573
|
+
|
|
1574
|
+
class ContextMenuTriggerDirective {
|
|
1575
|
+
elementRef;
|
|
1576
|
+
appContextMenu = input.required(...(ngDevMode ? [{ debugName: "appContextMenu" }] : []));
|
|
1577
|
+
beforeOpen = output();
|
|
1578
|
+
constructor(elementRef) {
|
|
1579
|
+
this.elementRef = elementRef;
|
|
1580
|
+
}
|
|
1581
|
+
onContextMenu(event) {
|
|
1582
|
+
event.preventDefault();
|
|
1583
|
+
event.stopPropagation();
|
|
1584
|
+
// Notify parent to prepare data
|
|
1585
|
+
this.beforeOpen.emit();
|
|
1586
|
+
// Show menu at cursor position
|
|
1587
|
+
this.appContextMenu().show(event.clientX, event.clientY);
|
|
1588
|
+
}
|
|
1589
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ContextMenuTriggerDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
1590
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.0", type: ContextMenuTriggerDirective, isStandalone: true, selector: "[appContextMenu]", inputs: { appContextMenu: { classPropertyName: "appContextMenu", publicName: "appContextMenu", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { beforeOpen: "beforeOpen" }, host: { listeners: { "contextmenu": "onContextMenu($event)" } }, ngImport: i0 });
|
|
1591
|
+
}
|
|
1592
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ContextMenuTriggerDirective, decorators: [{
|
|
1593
|
+
type: Directive,
|
|
1594
|
+
args: [{
|
|
1595
|
+
selector: '[appContextMenu]',
|
|
1596
|
+
standalone: true
|
|
1597
|
+
}]
|
|
1598
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { appContextMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "appContextMenu", required: true }] }], beforeOpen: [{ type: i0.Output, args: ["beforeOpen"] }], onContextMenu: [{
|
|
1599
|
+
type: HostListener,
|
|
1600
|
+
args: ['contextmenu', ['$event']]
|
|
1601
|
+
}] } });
|
|
1602
|
+
|
|
1603
|
+
/**
|
|
1604
|
+
* Generated bundle index. Do not edit.
|
|
1605
|
+
*/
|
|
1606
|
+
|
|
1607
|
+
export { Async, BackOnClickDirective, CancellationError, CancellationTokenSource, CapacitorFilesService, ConsoleHook, ContextButtonComponent, ContextHeaderComponent, ContextMenuComponent, ContextMenuTriggerDirective, ECHO, ECHO_PROVIDER, Echo, EchoConsole, EchoLogger, EchoSettings, EchoSystemLogger, FileBoxService, FilesService, KeySignals, LocalStorageFilesService, LocalizationService, LocalizePipe, LogLevel, LogSystems, LogWriterConfig, NavigationStackService, ServicesInit, SettingsService, SettingsSignalsService, SystemColor, TauriFilesService, ThemeService, ThemeType, echoFactory };
|
|
1608
|
+
//# sourceMappingURL=orcas-angular.mjs.map
|