orcas-angular 1.0.5 → 1.0.6
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/async/README.md +46 -0
- package/dev/README.md +41 -0
- package/framework/README.md +34 -0
- package/localization/README.md +73 -0
- package/log/README.md +275 -0
- package/navigation/README.md +47 -0
- package/package.json +1 -1
- package/storage/README.md +75 -0
- package/theme/README.md +44 -0
- package/ui/README.md +42 -0
package/async/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# async
|
|
2
|
+
|
|
3
|
+
Utilities for working with asynchronous operations.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
### `async.ts`
|
|
8
|
+
|
|
9
|
+
The `Async` class provides static helper methods for common async patterns:
|
|
10
|
+
|
|
11
|
+
- **`Async.delay(ms)`** — Returns a `Promise` that resolves after a given number of milliseconds. Useful for introducing deliberate pauses in async flows.
|
|
12
|
+
- **`Async.until(check, timeoutMs?, frequencyMs?)`** — Polls a condition function at a given interval until it returns `true`. Throws a `Error` if the timeout is exceeded. Defaults to a 10-second timeout and 100 ms polling frequency.
|
|
13
|
+
|
|
14
|
+
### `cancellation-token.ts`
|
|
15
|
+
|
|
16
|
+
Provides a cooperative cancellation mechanism inspired by .NET's `CancellationToken` pattern:
|
|
17
|
+
|
|
18
|
+
- **`CancellationToken`** *(interface)* — Read-only token that exposes `isCancelled()` and `throwIfCancelled()`. Passed to async operations to signal cancellation without forcing an abort.
|
|
19
|
+
- **`CancellationTokenSource`** — Owns and controls a `CancellationToken`. Call `cancel()` to signal cancellation, or `newUnique(timeoutMs?)` to cancel the previous token and issue a fresh one (optionally with an auto-cancel timeout).
|
|
20
|
+
- **`CancellationError`** — Error subclass thrown by `throwIfCancelled()` when cancellation has been requested.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { Async } from '@/lib/orcas-angular/async/async';
|
|
26
|
+
import { CancellationTokenSource } from '@/lib/orcas-angular/async/cancellation-token';
|
|
27
|
+
|
|
28
|
+
// Delay example
|
|
29
|
+
await Async.delay(500);
|
|
30
|
+
|
|
31
|
+
// Poll until condition
|
|
32
|
+
await Async.until(() => someFlag === true);
|
|
33
|
+
|
|
34
|
+
// Cancellation example
|
|
35
|
+
const cts = new CancellationTokenSource();
|
|
36
|
+
const token = cts.newUnique(5000); // auto-cancels after 5 s
|
|
37
|
+
|
|
38
|
+
async function doWork() {
|
|
39
|
+
token.throwIfCancelled();
|
|
40
|
+
await Async.delay(100);
|
|
41
|
+
token.throwIfCancelled();
|
|
42
|
+
// ...
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
cts.cancel(); // cancel from outside
|
|
46
|
+
```
|
package/dev/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# dev
|
|
2
|
+
|
|
3
|
+
Development and debugging utilities. These helpers are meant for use during development only and should not be shipped as core application services.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
### `console-hook.ts`
|
|
8
|
+
|
|
9
|
+
`ConsoleHook` exposes a lightweight command registry that can be invoked from the browser console. It registers a global `window.r(command, ...args)` function so that developers can call application methods without needing to open Angular DevTools.
|
|
10
|
+
|
|
11
|
+
**Methods:**
|
|
12
|
+
- **`ConsoleHook.initialize()`** — Sets up `window.r` if it has not been set up yet.
|
|
13
|
+
- **`ConsoleHook.register(commandName, method)`** — Registers a named command. Automatically calls `initialize()`.
|
|
14
|
+
- **`ConsoleHook.run(input, ...additionalParams)`** — Parses the input string (command name + space-separated arguments) and executes the matching registered command.
|
|
15
|
+
|
|
16
|
+
**Browser console usage:**
|
|
17
|
+
```javascript
|
|
18
|
+
// Call a registered command named "example" with no extra args
|
|
19
|
+
r("example")
|
|
20
|
+
|
|
21
|
+
// Call "myCommand" with arguments
|
|
22
|
+
r("myCommand arg1 arg2")
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### `debug.service.ts.example`
|
|
26
|
+
|
|
27
|
+
A template showing how to integrate `ConsoleHook` into an Angular service. Copy this file, rename it to `debug.service.ts`, and adapt it to register the commands relevant to your application.
|
|
28
|
+
|
|
29
|
+
The example registers two commands:
|
|
30
|
+
- `"example"` — returns the injected `ExampleService` instance.
|
|
31
|
+
- `"nop"` — logs `"nop"` to the console and returns `"1"`.
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { ConsoleHook } from '@/lib/orcas-angular/dev/console-hook';
|
|
37
|
+
|
|
38
|
+
// In any service or component constructor:
|
|
39
|
+
ConsoleHook.register('reload', () => location.reload());
|
|
40
|
+
ConsoleHook.register('clearStorage', () => localStorage.clear());
|
|
41
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# framework
|
|
2
|
+
|
|
3
|
+
Angular framework utilities that help with application startup and service lifecycle management.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
### `services-init.ts`
|
|
8
|
+
|
|
9
|
+
`ServicesInit` is a root-level Angular service that provides a typed, async-aware way to retrieve and initialize other services during application bootstrap.
|
|
10
|
+
|
|
11
|
+
**Why it exists:** Angular's DI container instantiates services lazily and synchronously. `ServicesInit` bridges the gap when a service needs async initialization (e.g. loading a file, fetching config) before the app is ready to use it.
|
|
12
|
+
|
|
13
|
+
**Method:**
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
async init<T>(serviceClass: any, ...params: unknown[]): Promise<T>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- Retrieves the service instance from the injector.
|
|
20
|
+
- If the service has an `init(...params)` method, calls it and awaits completion.
|
|
21
|
+
- If initialization parameters are provided to a service that has no `init` method, an error is thrown.
|
|
22
|
+
- Returns the fully initialized service instance, typed as `T`.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { ServicesInit } from '@/lib/orcas-angular/framework/services-init';
|
|
28
|
+
|
|
29
|
+
// In your app initialization (e.g. APP_INITIALIZER or main bootstrap):
|
|
30
|
+
const servicesInit = injector.get(ServicesInit);
|
|
31
|
+
|
|
32
|
+
await servicesInit.init(FileBoxService, 'app-data.json');
|
|
33
|
+
await servicesInit.init(LocalizationService, 'assets/translations.json', 'en');
|
|
34
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# localization
|
|
2
|
+
|
|
3
|
+
> A README.md is also available in the `localization` folder.
|
|
4
|
+
|
|
5
|
+
Angular service and pipe for loading and applying multi-language translations at runtime.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
### `localization.interface.ts`
|
|
10
|
+
|
|
11
|
+
`ILocalizationService` — interface defining the public contract that any localization service implementation must satisfy.
|
|
12
|
+
|
|
13
|
+
### `localization.service.ts`
|
|
14
|
+
|
|
15
|
+
`LocalizationService` is the concrete implementation. It loads a JSON translation file over HTTP and exposes a reactive API based on Angular signals.
|
|
16
|
+
|
|
17
|
+
**Key features:**
|
|
18
|
+
- Loads translations from a configurable JSON file path (default: `assets/translations.json`).
|
|
19
|
+
- Persists the active language in `localStorage` and restores it on startup.
|
|
20
|
+
- Supports **nested keys** via dot notation (e.g. `"settings.title"`).
|
|
21
|
+
- Supports **pluralization** via the `__1` suffix convention (e.g. a key named `"items__1"` is used when `params.count === 1`).
|
|
22
|
+
- Supports **parameter substitution** using `{{index}}` (array) or `{{key}}` (object) placeholders.
|
|
23
|
+
- Falls back to the default language if a key is missing for the active language.
|
|
24
|
+
|
|
25
|
+
**Reactive signal:**
|
|
26
|
+
- **`$currentLang`** — Computed signal that emits whenever the active language changes.
|
|
27
|
+
|
|
28
|
+
**Methods:**
|
|
29
|
+
|
|
30
|
+
| Method | Description |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `init(jsonPath?, defaultLanguage?, storageKey?)` | Loads the translation file and configures defaults. Call during app bootstrap. |
|
|
33
|
+
| `getLanguage()` | Returns the current language code. |
|
|
34
|
+
| `getDefaultLanguage()` | Returns the default/fallback language code. |
|
|
35
|
+
| `setActiveLanguage(lang)` | Switches the active language and persists it. |
|
|
36
|
+
| `translate(key, params?, language?)` | Resolves a translation key with optional parameter substitution. |
|
|
37
|
+
|
|
38
|
+
### `localize.pipe.ts`
|
|
39
|
+
|
|
40
|
+
`LocalizePipe` (`| localize`) is an impure standalone pipe that wraps `LocalizationService.translate()`. It re-evaluates only when the language, key, or params change, keeping re-render cost minimal.
|
|
41
|
+
|
|
42
|
+
## Translation file format
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"greeting": {
|
|
47
|
+
"en": "Hello, {{name}}!",
|
|
48
|
+
"es": "¡Hola, {{name}}!"
|
|
49
|
+
},
|
|
50
|
+
"items": {
|
|
51
|
+
"en": "{{count}} items",
|
|
52
|
+
"es": "{{count}} elementos"
|
|
53
|
+
},
|
|
54
|
+
"items__1": {
|
|
55
|
+
"en": "1 item",
|
|
56
|
+
"es": "1 elemento"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// Bootstrap (e.g. in APP_INITIALIZER):
|
|
65
|
+
await servicesInit.init(LocalizationService, 'assets/translations.json', 'en');
|
|
66
|
+
|
|
67
|
+
// In a template:
|
|
68
|
+
{{ 'greeting' | localize: { name: 'World' } }}
|
|
69
|
+
|
|
70
|
+
// Programmatically:
|
|
71
|
+
localizationService.translate('items', { count: 3 });
|
|
72
|
+
localizationService.setActiveLanguage('es');
|
|
73
|
+
```
|
package/log/README.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Echo TypeScript
|
|
2
|
+
|
|
3
|
+
TypeScript port of the Echo.cs logging library. This is a flexible and powerful logging library with support for log levels, system-based organization, and custom log writers.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Structured logging with system tags
|
|
8
|
+
- Customizable log levels (per system or global)
|
|
9
|
+
- String formatting with parameters (only formatted when log will be written)
|
|
10
|
+
- Log-once functionality to prevent duplicate messages
|
|
11
|
+
- Extensible with custom log writers
|
|
12
|
+
- Console log writer with colors and timestamps included
|
|
13
|
+
- TypeScript type safety
|
|
14
|
+
- Performance optimized - no allocations when logs are filtered out
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Install dependencies
|
|
20
|
+
npm install
|
|
21
|
+
|
|
22
|
+
# Build the library
|
|
23
|
+
npm run build
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The compiled JavaScript and type definitions will be in the `dist/` folder.
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { EchoConsole, LogLevel } from './dist/echo';
|
|
32
|
+
|
|
33
|
+
// Create an Echo instance with default console writer
|
|
34
|
+
const echo = EchoConsole.new();
|
|
35
|
+
|
|
36
|
+
// Get a logger
|
|
37
|
+
const logger = echo.getLogger();
|
|
38
|
+
|
|
39
|
+
// Log messages
|
|
40
|
+
logger.debug("System", "Debug message");
|
|
41
|
+
logger.info("System", "Info message");
|
|
42
|
+
logger.warn("System", "Warning message");
|
|
43
|
+
logger.error("System", "Error message");
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage Examples
|
|
47
|
+
|
|
48
|
+
### Basic Logging
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { EchoConsole } from './echo';
|
|
52
|
+
|
|
53
|
+
const echo = EchoConsole.new();
|
|
54
|
+
const logger = echo.getLogger();
|
|
55
|
+
|
|
56
|
+
// Log with different levels
|
|
57
|
+
logger.debug("GUI", "This is a debug message from the GUI system.");
|
|
58
|
+
logger.info("Physics", "This is an info message from the Physics system.");
|
|
59
|
+
logger.warn("AI", "This is a warning message from the AI system.");
|
|
60
|
+
logger.error("Rendering", "This is an error message from the Rendering system.");
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### System Logger
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Get a system-specific logger (cached per system)
|
|
67
|
+
const animationLogger = echo.getSystemLogger("Animation");
|
|
68
|
+
|
|
69
|
+
// No need to specify system in subsequent calls
|
|
70
|
+
animationLogger.debug("This is a debug message from the Animation system.");
|
|
71
|
+
animationLogger.info("This is an info message from the Animation system.");
|
|
72
|
+
animationLogger.warn("This is a warning message from the Animation system.");
|
|
73
|
+
animationLogger.error("This is an error message from the Animation system.");
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### String Formatting
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Use formatted strings with parameters
|
|
80
|
+
// Formatting is only done IF the log will be written (performance optimization)
|
|
81
|
+
const playerName = "John";
|
|
82
|
+
const playerHealth = 100;
|
|
83
|
+
logger.info("General", "Player {0} has {1} health.", playerName, playerHealth);
|
|
84
|
+
// Output: Player John has 100 health.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Log Level Management
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { LogLevel } from './echo';
|
|
91
|
+
|
|
92
|
+
// Set default log level (applies to all systems)
|
|
93
|
+
echo.settings.setDefaultLevel(LogLevel.Warn); // Only Warn and Error will be logged
|
|
94
|
+
|
|
95
|
+
// Set system-specific log level
|
|
96
|
+
echo.settings.setSystemLevel("Physics", LogLevel.Debug); // Physics logs everything
|
|
97
|
+
|
|
98
|
+
// Get current log level for a system
|
|
99
|
+
const level = echo.settings.getSystemLevel("Physics");
|
|
100
|
+
|
|
101
|
+
// Clear a system-specific level (reverts to default)
|
|
102
|
+
echo.settings.clearSystemLevel("Physics");
|
|
103
|
+
|
|
104
|
+
// Clear all system-specific levels
|
|
105
|
+
echo.settings.clearSystemLevels();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Log Once
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Log a message only once, even if called multiple times
|
|
112
|
+
logger.debug1("System", "This will only appear once");
|
|
113
|
+
logger.debug1("System", "This will only appear once"); // Won't be logged
|
|
114
|
+
logger.debug1("System", "This will only appear once"); // Won't be logged
|
|
115
|
+
|
|
116
|
+
// Also works with other log levels
|
|
117
|
+
logger.info1("System", "Info once");
|
|
118
|
+
logger.warn1("System", "Warn once");
|
|
119
|
+
logger.error1("System", "Error once");
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Custom Configuration
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { LogWriterConfig, SystemColor } from './echo';
|
|
126
|
+
|
|
127
|
+
const config = new LogWriterConfig();
|
|
128
|
+
config.timestamp = true; // Include timestamps (default: true)
|
|
129
|
+
config.levelLabels = true; // Include log level labels (default: true)
|
|
130
|
+
config.levelColors = true; // Use colors for log levels (default: true)
|
|
131
|
+
config.systemColor = SystemColor.LabelAndMessage; // Color both label and message
|
|
132
|
+
|
|
133
|
+
const echo = EchoConsole.new(config);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Custom Log Writer
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { Echo, EchoLogWriter, LogLevel } from './echo';
|
|
140
|
+
|
|
141
|
+
class CustomLogWriter implements EchoLogWriter {
|
|
142
|
+
writeLog(level: LogLevel, system: string, message: string): void {
|
|
143
|
+
// Custom log writing logic here
|
|
144
|
+
const timestamp = new Date().toISOString();
|
|
145
|
+
console.log(`${timestamp} | ${level} | [${system}] ${message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Use custom writer
|
|
150
|
+
const customWriter = new CustomLogWriter();
|
|
151
|
+
const echo = new Echo(customWriter);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## API Reference
|
|
155
|
+
|
|
156
|
+
### Echo Class
|
|
157
|
+
|
|
158
|
+
Main entry point for the library.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
constructor(writer: EchoLogWriter)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
- **getLogger()**: Returns the main logger instance (cached)
|
|
165
|
+
- **getSystemLogger(system: string)**: Returns a system-specific logger (cached per system)
|
|
166
|
+
- **settings**: Access to EchoSettings for configuration
|
|
167
|
+
|
|
168
|
+
### EchoLogger Class
|
|
169
|
+
|
|
170
|
+
Main logger with system parameter required.
|
|
171
|
+
|
|
172
|
+
**Methods** (all have the same signature pattern):
|
|
173
|
+
- **debug(system: string, message: string, ...params: any[])**
|
|
174
|
+
- **info(system: string, message: string, ...params: any[])**
|
|
175
|
+
- **warn(system: string, message: string, ...params: any[])**
|
|
176
|
+
- **error(system: string, message: string, ...params: any[])**
|
|
177
|
+
|
|
178
|
+
**Log-once variants** (append `1` to method name):
|
|
179
|
+
- **debug1, info1, warn1, error1** - Same signatures as above
|
|
180
|
+
|
|
181
|
+
### EchoSystemLogger Class
|
|
182
|
+
|
|
183
|
+
System-specific logger (system is set at creation).
|
|
184
|
+
|
|
185
|
+
**Methods** (no system parameter needed):
|
|
186
|
+
- **debug(message: string, ...params: any[])**
|
|
187
|
+
- **info(message: string, ...params: any[])**
|
|
188
|
+
- **warn(message: string, ...params: any[])**
|
|
189
|
+
- **error(message: string, ...params: any[])**
|
|
190
|
+
|
|
191
|
+
**Log-once variants**:
|
|
192
|
+
- **debug1, info1, warn1, error1** - Same signatures as above
|
|
193
|
+
|
|
194
|
+
### EchoSettings Class
|
|
195
|
+
|
|
196
|
+
Configuration for log levels.
|
|
197
|
+
|
|
198
|
+
- **defaultLevel**: Get/set the default log level
|
|
199
|
+
- **setDefaultLevel(level: LogLevel)**: Set default log level for all systems
|
|
200
|
+
- **setSystemLevel(system: string, level: LogLevel)**: Set log level for specific system
|
|
201
|
+
- **getSystemLevel(system: string)**: Get log level for specific system
|
|
202
|
+
- **clearSystemLevel(system: string)**: Remove system-specific level
|
|
203
|
+
- **clearSystemLevels()**: Remove all system-specific levels
|
|
204
|
+
- **getAllSystemLevels()**: Get all system-specific levels
|
|
205
|
+
- **onUpdated(callback: () => void)**: Register callback for settings updates
|
|
206
|
+
|
|
207
|
+
### LogLevel Enum
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
enum LogLevel {
|
|
211
|
+
None = 0, // No logging
|
|
212
|
+
Error = 1, // Errors only
|
|
213
|
+
Warn = 2, // Warnings and errors
|
|
214
|
+
Info = 3, // Info, warnings, and errors
|
|
215
|
+
Debug = 4 // All logs (most verbose)
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### LogWriterConfig Class
|
|
220
|
+
|
|
221
|
+
Configuration for the console log writer.
|
|
222
|
+
|
|
223
|
+
- **timestamp**: Include timestamps (default: true)
|
|
224
|
+
- **levelLabels**: Include log level labels (default: true)
|
|
225
|
+
- **levelColors**: Use colors for log levels (default: true)
|
|
226
|
+
- **systemColor**: Control system color usage (default: SystemColor.LabelOnly)
|
|
227
|
+
|
|
228
|
+
### SystemColor Enum
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
enum SystemColor {
|
|
232
|
+
None, // No color
|
|
233
|
+
LabelOnly, // Color only the system label
|
|
234
|
+
LabelAndMessage // Color both label and message
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### EchoConsole Helper
|
|
239
|
+
|
|
240
|
+
Factory for creating Echo instances with console writer.
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
EchoConsole.new(config?: LogWriterConfig): Echo
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Differences from C# Version
|
|
247
|
+
|
|
248
|
+
The TypeScript port maintains the same API signatures as the C# version with these differences:
|
|
249
|
+
|
|
250
|
+
1. **Case Convention**: TypeScript methods use camelCase (e.g., `getLogger()`) instead of PascalCase
|
|
251
|
+
2. **Generic Overloads**: Instead of C# generic type parameters, TypeScript uses rest parameters (`...params: any[]`)
|
|
252
|
+
3. **Events**: Instead of C# events, use `onUpdated(callback)` method to register callbacks
|
|
253
|
+
4. **Properties**: C# properties become TypeScript getters/setters
|
|
254
|
+
5. **No Unity**: This port excludes all Unity-specific features
|
|
255
|
+
|
|
256
|
+
## Running the Demo
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Compile
|
|
260
|
+
tsc echo.demo.ts
|
|
261
|
+
|
|
262
|
+
# Run
|
|
263
|
+
node echo.demo.js
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Running Tests
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Compile and run tests
|
|
270
|
+
tsc && node echo.test.js
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
Copyright © 2025 Racso
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# navigation
|
|
2
|
+
|
|
3
|
+
Angular utilities for managing navigation history and providing a reliable "go back" behaviour.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
### `navigation-stack.service.ts`
|
|
8
|
+
|
|
9
|
+
`NavigationStackService` maintains an in-memory history stack by subscribing to Angular Router's `NavigationEnd` events. This gives the application precise control over back-navigation, including a safe fallback to the home route when the history is empty.
|
|
10
|
+
|
|
11
|
+
**Methods:**
|
|
12
|
+
- **`goBack()`** — Navigates to the previous route. Falls back to `"/"` if there is no history.
|
|
13
|
+
- **`getBack(): string`** — Returns the URL of the previous route without navigating, or an empty string if unavailable.
|
|
14
|
+
|
|
15
|
+
### `back-on-click.directive.ts`
|
|
16
|
+
|
|
17
|
+
`BackOnClickDirective` (`[back-on-click]`) is a standalone directive that calls `NavigationStackService.goBack()` when the host element is clicked. It also prevents the default click behaviour so it can be safely applied to `<a>` elements.
|
|
18
|
+
|
|
19
|
+
### `index.ts`
|
|
20
|
+
|
|
21
|
+
Public barrel that re-exports both `NavigationStackService` and `BackOnClickDirective`.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// In a standalone component:
|
|
27
|
+
import { BackOnClickDirective } from '@/lib/orcas-angular/navigation';
|
|
28
|
+
|
|
29
|
+
@Component({
|
|
30
|
+
imports: [BackOnClickDirective],
|
|
31
|
+
template: `<button back-on-click>← Back</button>`
|
|
32
|
+
})
|
|
33
|
+
export class MyComponent {}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Programmatic back navigation:
|
|
38
|
+
import { NavigationStackService } from '@/lib/orcas-angular/navigation';
|
|
39
|
+
|
|
40
|
+
export class MyComponent {
|
|
41
|
+
private nav = inject(NavigationStackService);
|
|
42
|
+
|
|
43
|
+
onClose() {
|
|
44
|
+
this.nav.goBack();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
package/package.json
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# storage
|
|
2
|
+
|
|
3
|
+
Reactive, file-backed storage layer for application and repository settings. Built on Angular signals and an abstract file service that can be backed by different platforms (Tauri, Capacitor, or `localStorage`).
|
|
4
|
+
|
|
5
|
+
## Architecture overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
FilesService (abstract)
|
|
9
|
+
├── TauriFilesService — Tauri desktop file system
|
|
10
|
+
├── CapacitorFilesService — Capacitor mobile file system
|
|
11
|
+
└── LocalStorageFilesService — Browser localStorage (fallback)
|
|
12
|
+
|
|
13
|
+
FileBoxService — reads/writes a single JSON file via FilesService
|
|
14
|
+
└── SettingsSignalsService (extends KeySignals)
|
|
15
|
+
└── SettingsService — scoped to the "app-settings" key
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Files
|
|
19
|
+
|
|
20
|
+
### `files.ts`
|
|
21
|
+
|
|
22
|
+
Abstract `FilesService` class. Defines the platform-agnostic contract for file operations:
|
|
23
|
+
|
|
24
|
+
| Method | Description |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `init(...args)` | Platform-specific setup |
|
|
27
|
+
| `joinStoragePath(filePath)` | Resolves a path relative to the storage root |
|
|
28
|
+
| `hasInStorage(filePath)` | Checks existence in writable storage |
|
|
29
|
+
| `readFromStorage(filePath)` | Reads a file from writable storage |
|
|
30
|
+
| `writeToStorage(filePath, data)` | Writes a file to writable storage |
|
|
31
|
+
| `hasInProject(filePath)` | Checks existence in project assets |
|
|
32
|
+
| `readFromProject(filePath)` | Reads a file from project assets |
|
|
33
|
+
|
|
34
|
+
### `tauri-files.service.ts` / `capacitor-files.service.ts` / `local-storage-files.service.ts`
|
|
35
|
+
|
|
36
|
+
Concrete implementations of `FilesService` for Tauri, Capacitor, and browser `localStorage`.
|
|
37
|
+
|
|
38
|
+
### `file-box.service.ts`
|
|
39
|
+
|
|
40
|
+
`FileBoxService` loads and persists an entire JSON file as a flat key→value map. It exposes a reactive `$data` signal so that anything that reads from the box re-renders automatically when data changes. Saves are debounced so that multiple rapid changes are flushed in a single write.
|
|
41
|
+
|
|
42
|
+
**Init:** must be called once during bootstrap: `init(path: string)`.
|
|
43
|
+
|
|
44
|
+
### `key-signals.ts`
|
|
45
|
+
|
|
46
|
+
Abstract `KeySignals` base class. Provides a hierarchical key system where multiple string path segments are joined with a `|` separator to form a flat storage key. Subclasses implement the three abstract methods to wire `KeySignals` to an actual storage backend.
|
|
47
|
+
|
|
48
|
+
**API:**
|
|
49
|
+
|
|
50
|
+
| Method | Description |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `getNewSignal<T>(defaultValue, ...path)` | Returns a computed `Signal<T>` for the given path |
|
|
53
|
+
| `getValue<T>(defaultValue, ...path)` | Reads the value synchronously |
|
|
54
|
+
| `set(value, ...path)` | Persists a value at the given path |
|
|
55
|
+
| `clearByPrefix(...pathPrefix)` | Removes all keys that share a common prefix |
|
|
56
|
+
|
|
57
|
+
### `settings-signals.service.ts`
|
|
58
|
+
|
|
59
|
+
`SettingsSignalsService` extends `KeySignals` and connects it to `FileBoxService`. All reads and writes go through the reactive `$data` signal and the file-save pipeline.
|
|
60
|
+
|
|
61
|
+
### `settings.service.ts`
|
|
62
|
+
|
|
63
|
+
`SettingsService` wraps `SettingsSignalsService` with a fixed `"app-settings"` namespace, providing a clean API for global application settings.
|
|
64
|
+
|
|
65
|
+
### `settings.service.spec.ts` / `signal-caching.spec.ts` / `key-signals.spec.ts`
|
|
66
|
+
|
|
67
|
+
Unit tests for the storage layer.
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Global app setting (persists across sessions)
|
|
73
|
+
const isDarkMode = settingsService.getNewSignal(false, 'theme', 'dark');
|
|
74
|
+
await settingsService.set(true, 'theme', 'dark');
|
|
75
|
+
```
|
package/theme/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# theme
|
|
2
|
+
|
|
3
|
+
Angular service for managing the application's visual theme (light / dark mode).
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
### `theme.service.ts`
|
|
8
|
+
|
|
9
|
+
`ThemeService` reads and persists the user's theme preference via `SettingsService` and reflects it on the DOM in real time.
|
|
10
|
+
|
|
11
|
+
**Signals:**
|
|
12
|
+
- **`$theme`** — Reactive signal (`Signal<ThemeType>`) holding the current theme preference. Backed by persistent storage so the user's choice survives page reloads.
|
|
13
|
+
- **`$darkMode`** — Computed `boolean` signal. Returns `true` when the effective theme is dark. When the stored preference is `ThemeType.Unset`, it automatically falls back to the OS-level `prefers-color-scheme: dark` media query.
|
|
14
|
+
|
|
15
|
+
Whenever `$darkMode` changes, an Angular `effect` toggles the `dark` CSS class on `document.documentElement`, making the service compatible with Tailwind CSS's `darkMode: 'class'` strategy (or any similar class-based dark-mode approach).
|
|
16
|
+
|
|
17
|
+
**Method:**
|
|
18
|
+
- **`setTheme(theme: ThemeType)`** — Persists the chosen theme and updates the `$theme` signal.
|
|
19
|
+
|
|
20
|
+
**`ThemeType` enum:**
|
|
21
|
+
|
|
22
|
+
| Value | Description |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `ThemeType.Unset` | Follow the OS preference |
|
|
25
|
+
| `ThemeType.Light` | Force light mode |
|
|
26
|
+
| `ThemeType.Dark` | Force dark mode |
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { ThemeService, ThemeType } from '@/lib/orcas-angular/theme/theme.service';
|
|
32
|
+
|
|
33
|
+
export class SettingsComponent {
|
|
34
|
+
private theme = inject(ThemeService);
|
|
35
|
+
|
|
36
|
+
// Read current dark-mode state reactively
|
|
37
|
+
isDark = this.theme.$darkMode;
|
|
38
|
+
|
|
39
|
+
async toggleTheme() {
|
|
40
|
+
const next = this.theme.$darkMode() ? ThemeType.Light : ThemeType.Dark;
|
|
41
|
+
await this.theme.setTheme(next);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
package/ui/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# ui
|
|
2
|
+
|
|
3
|
+
Reusable UI components for the application. Currently contains the context menu system.
|
|
4
|
+
|
|
5
|
+
## Folders
|
|
6
|
+
|
|
7
|
+
### `context-menu/`
|
|
8
|
+
|
|
9
|
+
A fully self-contained context menu implementation built with Angular standalone components and directives. It handles viewport clamping automatically so that menus never render off-screen, and supports nested sub-menus.
|
|
10
|
+
|
|
11
|
+
**Components and directives:**
|
|
12
|
+
|
|
13
|
+
| File | Selector | Description |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `context-menu.component.ts` | `<context-menu>` | Container that renders a floating menu panel at a given (x, y) coordinate. Can be used as a top-level menu or as an `isSubmenu` variant that positions itself adjacent to the parent button. |
|
|
16
|
+
| `context-button.component.ts` | `<context-button>` | Menu item button. Supports `danger` and `disabled` inputs and an optional `hasSubmenu` flag that reveals a `▶` indicator and conditionally renders a nested `<context-menu>` on hover. |
|
|
17
|
+
| `context-header.component.ts` | `<context-header>` | Non-interactive section header / label for grouping menu items. |
|
|
18
|
+
| `context-menu-trigger.directive.ts` | `[appContextMenu]` | Applied to any element. Intercepts the `contextmenu` event, prevents the default browser menu, and calls `show(x, y)` on the bound `ContextMenuComponent`. Emits a `beforeOpen` output before the menu opens, which is useful for preparing dynamic menu content. |
|
|
19
|
+
| `index.ts` | — | Public barrel re-exporting all of the above. |
|
|
20
|
+
|
|
21
|
+
**Usage:**
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<div [appContextMenu]="menu" (beforeOpen)="prepareMenu()">
|
|
25
|
+
Right-click me
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<context-menu #menu>
|
|
29
|
+
<context-header>Actions</context-header>
|
|
30
|
+
|
|
31
|
+
<context-button (click)="doSomething()">Do something</context-button>
|
|
32
|
+
|
|
33
|
+
<context-button danger (click)="deleteItem()">Delete</context-button>
|
|
34
|
+
|
|
35
|
+
<context-button [hasSubmenu]="true">
|
|
36
|
+
More…
|
|
37
|
+
<context-menu [isSubmenu]="true">
|
|
38
|
+
<context-button (click)="doNested()">Nested action</context-button>
|
|
39
|
+
</context-menu>
|
|
40
|
+
</context-button>
|
|
41
|
+
</context-menu>
|
|
42
|
+
```
|