@plures/praxis 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FRAMEWORK.md +420 -0
- package/LICENSE +21 -0
- package/README.md +1310 -0
- package/dist/adapters/cli.d.ts +43 -0
- package/dist/adapters/cli.d.ts.map +1 -0
- package/dist/adapters/cli.js +126 -0
- package/dist/adapters/cli.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +26 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +233 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/cloud.d.ts +27 -0
- package/dist/cli/commands/cloud.d.ts.map +1 -0
- package/dist/cli/commands/cloud.js +232 -0
- package/dist/cli/commands/cloud.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +25 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +168 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +179 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cloud/auth.d.ts +51 -0
- package/dist/cloud/auth.d.ts.map +1 -0
- package/dist/cloud/auth.js +194 -0
- package/dist/cloud/auth.js.map +1 -0
- package/dist/cloud/billing.d.ts +184 -0
- package/dist/cloud/billing.d.ts.map +1 -0
- package/dist/cloud/billing.js +179 -0
- package/dist/cloud/billing.js.map +1 -0
- package/dist/cloud/client.d.ts +39 -0
- package/dist/cloud/client.d.ts.map +1 -0
- package/dist/cloud/client.js +176 -0
- package/dist/cloud/client.js.map +1 -0
- package/dist/cloud/index.d.ts +44 -0
- package/dist/cloud/index.d.ts.map +1 -0
- package/dist/cloud/index.js +44 -0
- package/dist/cloud/index.js.map +1 -0
- package/dist/cloud/marketplace.d.ts +166 -0
- package/dist/cloud/marketplace.d.ts.map +1 -0
- package/dist/cloud/marketplace.js +159 -0
- package/dist/cloud/marketplace.js.map +1 -0
- package/dist/cloud/provisioning.d.ts +110 -0
- package/dist/cloud/provisioning.d.ts.map +1 -0
- package/dist/cloud/provisioning.js +148 -0
- package/dist/cloud/provisioning.js.map +1 -0
- package/dist/cloud/relay/endpoints.d.ts +62 -0
- package/dist/cloud/relay/endpoints.d.ts.map +1 -0
- package/dist/cloud/relay/endpoints.js +217 -0
- package/dist/cloud/relay/endpoints.js.map +1 -0
- package/dist/cloud/relay/health/index.d.ts +5 -0
- package/dist/cloud/relay/health/index.d.ts.map +1 -0
- package/dist/cloud/relay/health/index.js +9 -0
- package/dist/cloud/relay/health/index.js.map +1 -0
- package/dist/cloud/relay/stats/index.d.ts +5 -0
- package/dist/cloud/relay/stats/index.d.ts.map +1 -0
- package/dist/cloud/relay/stats/index.js +9 -0
- package/dist/cloud/relay/stats/index.js.map +1 -0
- package/dist/cloud/relay/sync/index.d.ts +5 -0
- package/dist/cloud/relay/sync/index.d.ts.map +1 -0
- package/dist/cloud/relay/sync/index.js +9 -0
- package/dist/cloud/relay/sync/index.js.map +1 -0
- package/dist/cloud/relay/usage/index.d.ts +5 -0
- package/dist/cloud/relay/usage/index.d.ts.map +1 -0
- package/dist/cloud/relay/usage/index.js +9 -0
- package/dist/cloud/relay/usage/index.js.map +1 -0
- package/dist/cloud/sponsors.d.ts +81 -0
- package/dist/cloud/sponsors.d.ts.map +1 -0
- package/dist/cloud/sponsors.js +130 -0
- package/dist/cloud/sponsors.js.map +1 -0
- package/dist/cloud/types.d.ts +169 -0
- package/dist/cloud/types.d.ts.map +1 -0
- package/dist/cloud/types.js +7 -0
- package/dist/cloud/types.js.map +1 -0
- package/dist/components/index.d.ts +43 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +17 -0
- package/dist/components/index.js.map +1 -0
- package/dist/core/actors.d.ts +95 -0
- package/dist/core/actors.d.ts.map +1 -0
- package/dist/core/actors.js +158 -0
- package/dist/core/actors.js.map +1 -0
- package/dist/core/component/generator.d.ts +122 -0
- package/dist/core/component/generator.d.ts.map +1 -0
- package/dist/core/component/generator.js +307 -0
- package/dist/core/component/generator.js.map +1 -0
- package/dist/core/engine.d.ts +92 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +199 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/introspection.d.ts +141 -0
- package/dist/core/introspection.d.ts.map +1 -0
- package/dist/core/introspection.js +208 -0
- package/dist/core/introspection.js.map +1 -0
- package/dist/core/logic/generator.d.ts +76 -0
- package/dist/core/logic/generator.d.ts.map +1 -0
- package/dist/core/logic/generator.js +339 -0
- package/dist/core/logic/generator.js.map +1 -0
- package/dist/core/pluresdb/generator.d.ts +58 -0
- package/dist/core/pluresdb/generator.d.ts.map +1 -0
- package/dist/core/pluresdb/generator.js +162 -0
- package/dist/core/pluresdb/generator.js.map +1 -0
- package/dist/core/protocol.d.ts +121 -0
- package/dist/core/protocol.d.ts.map +1 -0
- package/dist/core/protocol.js +46 -0
- package/dist/core/protocol.js.map +1 -0
- package/dist/core/rules.d.ts +120 -0
- package/dist/core/rules.d.ts.map +1 -0
- package/dist/core/rules.js +81 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/schema/loader.d.ts +47 -0
- package/dist/core/schema/loader.d.ts.map +1 -0
- package/dist/core/schema/loader.js +189 -0
- package/dist/core/schema/loader.js.map +1 -0
- package/dist/core/schema/normalize.d.ts +72 -0
- package/dist/core/schema/normalize.d.ts.map +1 -0
- package/dist/core/schema/normalize.js +190 -0
- package/dist/core/schema/normalize.js.map +1 -0
- package/dist/core/schema/types.d.ts +370 -0
- package/dist/core/schema/types.d.ts.map +1 -0
- package/dist/core/schema/types.js +161 -0
- package/dist/core/schema/types.js.map +1 -0
- package/dist/dsl/index.d.ts +152 -0
- package/dist/dsl/index.d.ts.map +1 -0
- package/dist/dsl/index.js +132 -0
- package/dist/dsl/index.js.map +1 -0
- package/dist/dsl.d.ts +124 -0
- package/dist/dsl.d.ts.map +1 -0
- package/dist/dsl.js +130 -0
- package/dist/dsl.js.map +1 -0
- package/dist/examples/advanced-todo/index.d.ts +55 -0
- package/dist/examples/advanced-todo/index.d.ts.map +1 -0
- package/dist/examples/advanced-todo/index.js +222 -0
- package/dist/examples/advanced-todo/index.js.map +1 -0
- package/dist/examples/auth-basic/index.d.ts +17 -0
- package/dist/examples/auth-basic/index.d.ts.map +1 -0
- package/dist/examples/auth-basic/index.js +122 -0
- package/dist/examples/auth-basic/index.js.map +1 -0
- package/dist/examples/cart/index.d.ts +19 -0
- package/dist/examples/cart/index.d.ts.map +1 -0
- package/dist/examples/cart/index.js +202 -0
- package/dist/examples/cart/index.js.map +1 -0
- package/dist/examples/hero-ecommerce/index.d.ts +39 -0
- package/dist/examples/hero-ecommerce/index.d.ts.map +1 -0
- package/dist/examples/hero-ecommerce/index.js +506 -0
- package/dist/examples/hero-ecommerce/index.js.map +1 -0
- package/dist/examples/svelte-counter/index.d.ts +31 -0
- package/dist/examples/svelte-counter/index.d.ts.map +1 -0
- package/dist/examples/svelte-counter/index.js +123 -0
- package/dist/examples/svelte-counter/index.js.map +1 -0
- package/dist/flows.d.ts +125 -0
- package/dist/flows.d.ts.map +1 -0
- package/dist/flows.js +160 -0
- package/dist/flows.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/pluresdb.d.ts +56 -0
- package/dist/integrations/pluresdb.d.ts.map +1 -0
- package/dist/integrations/pluresdb.js +46 -0
- package/dist/integrations/pluresdb.js.map +1 -0
- package/dist/integrations/svelte.d.ts +306 -0
- package/dist/integrations/svelte.d.ts.map +1 -0
- package/dist/integrations/svelte.js +447 -0
- package/dist/integrations/svelte.js.map +1 -0
- package/dist/registry.d.ts +94 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +181 -0
- package/dist/registry.js.map +1 -0
- package/dist/runtime/terminal-adapter.d.ts +105 -0
- package/dist/runtime/terminal-adapter.d.ts.map +1 -0
- package/dist/runtime/terminal-adapter.js +113 -0
- package/dist/runtime/terminal-adapter.js.map +1 -0
- package/dist/step.d.ts +34 -0
- package/dist/step.d.ts.map +1 -0
- package/dist/step.js +111 -0
- package/dist/step.js.map +1 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/docs/MONETIZATION.md +394 -0
- package/docs/TERMINAL_NODE.md +588 -0
- package/docs/guides/canvas.md +389 -0
- package/docs/guides/getting-started.md +347 -0
- package/docs/guides/history-state-pattern.md +618 -0
- package/docs/guides/orchestration.md +617 -0
- package/docs/guides/parallel-state-pattern.md +767 -0
- package/docs/guides/svelte-integration.md +691 -0
- package/package.json +96 -0
- package/src/__tests__/actors.test.ts +270 -0
- package/src/__tests__/billing.test.ts +175 -0
- package/src/__tests__/cloud.test.ts +247 -0
- package/src/__tests__/dsl.test.ts +154 -0
- package/src/__tests__/edge-cases.test.ts +475 -0
- package/src/__tests__/engine.test.ts +137 -0
- package/src/__tests__/generators.test.ts +270 -0
- package/src/__tests__/introspection.test.ts +321 -0
- package/src/__tests__/protocol.test.ts +40 -0
- package/src/__tests__/provisioning.test.ts +162 -0
- package/src/__tests__/schema.test.ts +241 -0
- package/src/__tests__/svelte-integration.test.ts +431 -0
- package/src/__tests__/terminal-node.test.ts +352 -0
- package/src/adapters/cli.ts +175 -0
- package/src/cli/commands/auth.ts +271 -0
- package/src/cli/commands/cloud.ts +281 -0
- package/src/cli/commands/generate.ts +225 -0
- package/src/cli/index.ts +190 -0
- package/src/cloud/README.md +383 -0
- package/src/cloud/auth.ts +245 -0
- package/src/cloud/billing.ts +336 -0
- package/src/cloud/client.ts +221 -0
- package/src/cloud/index.ts +121 -0
- package/src/cloud/marketplace.ts +303 -0
- package/src/cloud/provisioning.ts +254 -0
- package/src/cloud/relay/endpoints.ts +307 -0
- package/src/cloud/relay/health/function.json +17 -0
- package/src/cloud/relay/health/index.ts +10 -0
- package/src/cloud/relay/host.json +15 -0
- package/src/cloud/relay/local.settings.json +8 -0
- package/src/cloud/relay/stats/function.json +17 -0
- package/src/cloud/relay/stats/index.ts +10 -0
- package/src/cloud/relay/sync/function.json +17 -0
- package/src/cloud/relay/sync/index.ts +10 -0
- package/src/cloud/relay/usage/function.json +17 -0
- package/src/cloud/relay/usage/index.ts +10 -0
- package/src/cloud/sponsors.ts +213 -0
- package/src/cloud/types.ts +198 -0
- package/src/components/README.md +125 -0
- package/src/components/TerminalNode.svelte +457 -0
- package/src/components/index.ts +46 -0
- package/src/core/actors.ts +205 -0
- package/src/core/component/generator.ts +432 -0
- package/src/core/engine.ts +243 -0
- package/src/core/introspection.ts +329 -0
- package/src/core/logic/generator.ts +420 -0
- package/src/core/pluresdb/generator.ts +229 -0
- package/src/core/protocol.ts +132 -0
- package/src/core/rules.ts +167 -0
- package/src/core/schema/loader.ts +247 -0
- package/src/core/schema/normalize.ts +322 -0
- package/src/core/schema/types.ts +557 -0
- package/src/dsl/index.ts +218 -0
- package/src/dsl.ts +214 -0
- package/src/examples/advanced-todo/App.svelte +506 -0
- package/src/examples/advanced-todo/README.md +371 -0
- package/src/examples/advanced-todo/index.ts +309 -0
- package/src/examples/auth-basic/index.ts +163 -0
- package/src/examples/cart/index.ts +259 -0
- package/src/examples/hero-ecommerce/index.ts +657 -0
- package/src/examples/svelte-counter/index.ts +168 -0
- package/src/flows.ts +268 -0
- package/src/index.ts +154 -0
- package/src/integrations/pluresdb.ts +93 -0
- package/src/integrations/svelte.ts +617 -0
- package/src/registry.ts +223 -0
- package/src/runtime/terminal-adapter.ts +175 -0
- package/src/step.ts +151 -0
- package/src/types.ts +70 -0
- package/templates/basic-app/README.md +147 -0
- package/templates/fullstack-app/README.md +279 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* TerminalNode.svelte
|
|
4
|
+
*
|
|
5
|
+
* Svelte component for terminal nodes in Praxis/RuneBook.
|
|
6
|
+
* Provides a terminal interface with command execution, history, and canvas integration.
|
|
7
|
+
*/
|
|
8
|
+
import { onDestroy } from 'svelte';
|
|
9
|
+
import type { TerminalAdapter } from '../runtime/terminal-adapter.js';
|
|
10
|
+
import type { TerminalExecutionResult } from '../runtime/terminal-adapter.js';
|
|
11
|
+
|
|
12
|
+
// Props
|
|
13
|
+
export let adapter: TerminalAdapter;
|
|
14
|
+
export let x: number = 0;
|
|
15
|
+
export let y: number = 0;
|
|
16
|
+
export let width: number = 600;
|
|
17
|
+
export let height: number = 400;
|
|
18
|
+
export let draggable: boolean = true;
|
|
19
|
+
export let resizable: boolean = true;
|
|
20
|
+
export let showContextMenu: boolean = false;
|
|
21
|
+
|
|
22
|
+
// Local state
|
|
23
|
+
let currentCommand = '';
|
|
24
|
+
let isDragging = false;
|
|
25
|
+
let isResizing = false;
|
|
26
|
+
let dragStartX = 0;
|
|
27
|
+
let dragStartY = 0;
|
|
28
|
+
let resizeStartX = 0;
|
|
29
|
+
let resizeStartY = 0;
|
|
30
|
+
let resizeStartWidth = 0;
|
|
31
|
+
let resizeStartHeight = 0;
|
|
32
|
+
let terminalOutput: TerminalExecutionResult[] = [];
|
|
33
|
+
let contextMenuX = 0;
|
|
34
|
+
let contextMenuY = 0;
|
|
35
|
+
let outputContainer: HTMLDivElement;
|
|
36
|
+
|
|
37
|
+
// Get state from adapter
|
|
38
|
+
$: state = adapter.getState();
|
|
39
|
+
$: inputMode = state.inputMode;
|
|
40
|
+
$: history = state.history;
|
|
41
|
+
$: lastOutput = state.lastOutput;
|
|
42
|
+
|
|
43
|
+
// Execute command
|
|
44
|
+
async function executeCommand() {
|
|
45
|
+
if (!currentCommand.trim()) return;
|
|
46
|
+
|
|
47
|
+
const result = await adapter.executeCommand(currentCommand);
|
|
48
|
+
terminalOutput = [...terminalOutput, result];
|
|
49
|
+
currentCommand = '';
|
|
50
|
+
|
|
51
|
+
// Auto-scroll to bottom after DOM update
|
|
52
|
+
if (outputContainer) {
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
outputContainer.scrollTop = outputContainer.scrollHeight;
|
|
55
|
+
}, 0);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle keyboard shortcuts
|
|
60
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
61
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
executeCommand();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Drag handling
|
|
68
|
+
function startDrag(event: MouseEvent) {
|
|
69
|
+
if (!draggable) return;
|
|
70
|
+
isDragging = true;
|
|
71
|
+
dragStartX = event.clientX - x;
|
|
72
|
+
dragStartY = event.clientY - y;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function handleDrag(event: MouseEvent) {
|
|
76
|
+
if (!isDragging) return;
|
|
77
|
+
x = event.clientX - dragStartX;
|
|
78
|
+
y = event.clientY - dragStartY;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function stopDrag() {
|
|
82
|
+
isDragging = false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Resize handling
|
|
86
|
+
function startResize(event: MouseEvent) {
|
|
87
|
+
if (!resizable) return;
|
|
88
|
+
event.stopPropagation();
|
|
89
|
+
isResizing = true;
|
|
90
|
+
resizeStartX = event.clientX;
|
|
91
|
+
resizeStartY = event.clientY;
|
|
92
|
+
resizeStartWidth = width;
|
|
93
|
+
resizeStartHeight = height;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function handleResize(event: MouseEvent) {
|
|
97
|
+
if (!isResizing) return;
|
|
98
|
+
const deltaX = event.clientX - resizeStartX;
|
|
99
|
+
const deltaY = event.clientY - resizeStartY;
|
|
100
|
+
width = Math.max(300, resizeStartWidth + deltaX);
|
|
101
|
+
height = Math.max(200, resizeStartHeight + deltaY);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function stopResize() {
|
|
105
|
+
isResizing = false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Context menu handling
|
|
109
|
+
function handleContextMenu(event: MouseEvent) {
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
contextMenuX = event.clientX;
|
|
112
|
+
contextMenuY = event.clientY;
|
|
113
|
+
showContextMenu = true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function closeContextMenu() {
|
|
117
|
+
showContextMenu = false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function clearTerminal() {
|
|
121
|
+
terminalOutput = [];
|
|
122
|
+
adapter.clearHistory();
|
|
123
|
+
closeContextMenu();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function copyLastOutput() {
|
|
127
|
+
if (lastOutput) {
|
|
128
|
+
navigator.clipboard.writeText(lastOutput);
|
|
129
|
+
}
|
|
130
|
+
closeContextMenu();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Global mouse event listeners with proper cleanup
|
|
134
|
+
let mouseMoveHandler: ((e: MouseEvent) => void) | null = null;
|
|
135
|
+
let mouseUpHandler: (() => void) | null = null;
|
|
136
|
+
|
|
137
|
+
$: {
|
|
138
|
+
// Remove old listeners if they exist
|
|
139
|
+
if (mouseMoveHandler) {
|
|
140
|
+
window.removeEventListener('mousemove', mouseMoveHandler);
|
|
141
|
+
}
|
|
142
|
+
if (mouseUpHandler) {
|
|
143
|
+
window.removeEventListener('mouseup', mouseUpHandler);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Add new listeners if dragging or resizing
|
|
147
|
+
if (isDragging) {
|
|
148
|
+
mouseMoveHandler = handleDrag;
|
|
149
|
+
mouseUpHandler = stopDrag;
|
|
150
|
+
window.addEventListener('mousemove', mouseMoveHandler);
|
|
151
|
+
window.addEventListener('mouseup', mouseUpHandler);
|
|
152
|
+
} else if (isResizing) {
|
|
153
|
+
mouseMoveHandler = handleResize;
|
|
154
|
+
mouseUpHandler = stopResize;
|
|
155
|
+
window.addEventListener('mousemove', mouseMoveHandler);
|
|
156
|
+
window.addEventListener('mouseup', mouseUpHandler);
|
|
157
|
+
} else {
|
|
158
|
+
mouseMoveHandler = null;
|
|
159
|
+
mouseUpHandler = null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Cleanup event listeners on component destroy
|
|
164
|
+
onDestroy(() => {
|
|
165
|
+
if (mouseMoveHandler) {
|
|
166
|
+
window.removeEventListener('mousemove', mouseMoveHandler);
|
|
167
|
+
}
|
|
168
|
+
if (mouseUpHandler) {
|
|
169
|
+
window.removeEventListener('mouseup', mouseUpHandler);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Click outside to close context menu
|
|
174
|
+
function handleDocumentClick() {
|
|
175
|
+
if (showContextMenu) {
|
|
176
|
+
closeContextMenu();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<svelte:window on:click={handleDocumentClick} />
|
|
182
|
+
|
|
183
|
+
<div
|
|
184
|
+
class="terminal-node"
|
|
185
|
+
style="left: {x}px; top: {y}px; width: {width}px; height: {height}px;"
|
|
186
|
+
on:contextmenu={handleContextMenu}
|
|
187
|
+
role="application"
|
|
188
|
+
aria-label="Terminal Node"
|
|
189
|
+
>
|
|
190
|
+
<!-- Title bar -->
|
|
191
|
+
<div
|
|
192
|
+
class="terminal-header"
|
|
193
|
+
on:mousedown={startDrag}
|
|
194
|
+
role="banner"
|
|
195
|
+
>
|
|
196
|
+
<span class="terminal-title">Terminal: {state.nodeId}</span>
|
|
197
|
+
<span class="terminal-mode">Mode: {inputMode}</span>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<!-- Output area -->
|
|
201
|
+
<div class="terminal-output" bind:this={outputContainer}>
|
|
202
|
+
{#if terminalOutput.length === 0}
|
|
203
|
+
<div class="terminal-empty">No output yet. Enter a command below.</div>
|
|
204
|
+
{:else}
|
|
205
|
+
{#each terminalOutput as result}
|
|
206
|
+
<div class="terminal-result">
|
|
207
|
+
<div class="terminal-command">$ {result.command}</div>
|
|
208
|
+
<div class="terminal-result-output">{result.output}</div>
|
|
209
|
+
{#if result.error}
|
|
210
|
+
<div class="terminal-error">Error: {result.error}</div>
|
|
211
|
+
{/if}
|
|
212
|
+
</div>
|
|
213
|
+
{/each}
|
|
214
|
+
{/if}
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<!-- Input area -->
|
|
218
|
+
<div class="terminal-input-area">
|
|
219
|
+
<span class="terminal-prompt">$</span>
|
|
220
|
+
{#if inputMode === 'text'}
|
|
221
|
+
<input
|
|
222
|
+
type="text"
|
|
223
|
+
class="terminal-input"
|
|
224
|
+
bind:value={currentCommand}
|
|
225
|
+
on:keydown={handleKeyDown}
|
|
226
|
+
placeholder="Enter command..."
|
|
227
|
+
aria-label="Terminal input"
|
|
228
|
+
/>
|
|
229
|
+
{:else if inputMode === 'widget'}
|
|
230
|
+
<textarea
|
|
231
|
+
class="terminal-input terminal-input-widget"
|
|
232
|
+
bind:value={currentCommand}
|
|
233
|
+
on:keydown={handleKeyDown}
|
|
234
|
+
placeholder="Enter command..."
|
|
235
|
+
rows="1"
|
|
236
|
+
aria-label="Terminal input widget"
|
|
237
|
+
/>
|
|
238
|
+
{/if}
|
|
239
|
+
<button class="terminal-submit" on:click={executeCommand}>Run</button>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<!-- Resize handle -->
|
|
243
|
+
{#if resizable}
|
|
244
|
+
<div
|
|
245
|
+
class="terminal-resize-handle"
|
|
246
|
+
on:mousedown={startResize}
|
|
247
|
+
role="button"
|
|
248
|
+
tabindex="0"
|
|
249
|
+
aria-label="Resize terminal"
|
|
250
|
+
/>
|
|
251
|
+
{/if}
|
|
252
|
+
|
|
253
|
+
<!-- Context menu -->
|
|
254
|
+
{#if showContextMenu}
|
|
255
|
+
<div
|
|
256
|
+
class="terminal-context-menu"
|
|
257
|
+
style="left: {contextMenuX}px; top: {contextMenuY}px;"
|
|
258
|
+
role="menu"
|
|
259
|
+
>
|
|
260
|
+
<button class="context-menu-item" on:click={clearTerminal}>Clear Terminal</button>
|
|
261
|
+
<button class="context-menu-item" on:click={copyLastOutput} disabled={!lastOutput}>
|
|
262
|
+
Copy Last Output
|
|
263
|
+
</button>
|
|
264
|
+
<button class="context-menu-item" on:click={closeContextMenu}>Close Menu</button>
|
|
265
|
+
</div>
|
|
266
|
+
{/if}
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<style>
|
|
270
|
+
.terminal-node {
|
|
271
|
+
position: absolute;
|
|
272
|
+
display: flex;
|
|
273
|
+
flex-direction: column;
|
|
274
|
+
background: #1e1e1e;
|
|
275
|
+
border: 1px solid #3c3c3c;
|
|
276
|
+
border-radius: 8px;
|
|
277
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
278
|
+
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
279
|
+
color: #d4d4d4;
|
|
280
|
+
overflow: hidden;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.terminal-header {
|
|
284
|
+
display: flex;
|
|
285
|
+
justify-content: space-between;
|
|
286
|
+
align-items: center;
|
|
287
|
+
padding: 8px 12px;
|
|
288
|
+
background: #2d2d2d;
|
|
289
|
+
border-bottom: 1px solid #3c3c3c;
|
|
290
|
+
cursor: move;
|
|
291
|
+
user-select: none;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.terminal-title {
|
|
295
|
+
font-weight: 600;
|
|
296
|
+
font-size: 14px;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.terminal-mode {
|
|
300
|
+
font-size: 12px;
|
|
301
|
+
color: #858585;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.terminal-output {
|
|
305
|
+
flex: 1;
|
|
306
|
+
padding: 12px;
|
|
307
|
+
overflow-y: auto;
|
|
308
|
+
background: #1e1e1e;
|
|
309
|
+
font-size: 13px;
|
|
310
|
+
line-height: 1.6;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.terminal-empty {
|
|
314
|
+
color: #858585;
|
|
315
|
+
font-style: italic;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.terminal-result {
|
|
319
|
+
margin-bottom: 16px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.terminal-command {
|
|
323
|
+
color: #4ec9b0;
|
|
324
|
+
font-weight: 600;
|
|
325
|
+
margin-bottom: 4px;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.terminal-result-output {
|
|
329
|
+
color: #d4d4d4;
|
|
330
|
+
white-space: pre-wrap;
|
|
331
|
+
word-break: break-word;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.terminal-error {
|
|
335
|
+
color: #f48771;
|
|
336
|
+
margin-top: 4px;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.terminal-input-area {
|
|
340
|
+
display: flex;
|
|
341
|
+
align-items: center;
|
|
342
|
+
gap: 8px;
|
|
343
|
+
padding: 8px 12px;
|
|
344
|
+
background: #2d2d2d;
|
|
345
|
+
border-top: 1px solid #3c3c3c;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.terminal-prompt {
|
|
349
|
+
color: #4ec9b0;
|
|
350
|
+
font-weight: 600;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.terminal-input {
|
|
354
|
+
flex: 1;
|
|
355
|
+
padding: 6px 8px;
|
|
356
|
+
background: #3c3c3c;
|
|
357
|
+
border: 1px solid #515151;
|
|
358
|
+
border-radius: 4px;
|
|
359
|
+
color: #d4d4d4;
|
|
360
|
+
font-family: inherit;
|
|
361
|
+
font-size: 13px;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.terminal-input:focus {
|
|
365
|
+
outline: none;
|
|
366
|
+
border-color: #007acc;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.terminal-input-widget {
|
|
370
|
+
resize: vertical;
|
|
371
|
+
min-height: 28px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.terminal-submit {
|
|
375
|
+
padding: 6px 16px;
|
|
376
|
+
background: #007acc;
|
|
377
|
+
border: none;
|
|
378
|
+
border-radius: 4px;
|
|
379
|
+
color: white;
|
|
380
|
+
font-size: 13px;
|
|
381
|
+
font-weight: 600;
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
transition: background 0.2s;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.terminal-submit:hover {
|
|
387
|
+
background: #005a9e;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.terminal-submit:active {
|
|
391
|
+
background: #004578;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.terminal-resize-handle {
|
|
395
|
+
position: absolute;
|
|
396
|
+
bottom: 0;
|
|
397
|
+
right: 0;
|
|
398
|
+
width: 16px;
|
|
399
|
+
height: 16px;
|
|
400
|
+
cursor: nwse-resize;
|
|
401
|
+
background: linear-gradient(135deg, transparent 50%, #515151 50%);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.terminal-resize-handle:hover {
|
|
405
|
+
background: linear-gradient(135deg, transparent 50%, #007acc 50%);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.terminal-context-menu {
|
|
409
|
+
position: fixed;
|
|
410
|
+
background: #2d2d2d;
|
|
411
|
+
border: 1px solid #515151;
|
|
412
|
+
border-radius: 4px;
|
|
413
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
|
414
|
+
padding: 4px 0;
|
|
415
|
+
z-index: 1000;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.context-menu-item {
|
|
419
|
+
display: block;
|
|
420
|
+
width: 100%;
|
|
421
|
+
padding: 8px 16px;
|
|
422
|
+
background: transparent;
|
|
423
|
+
border: none;
|
|
424
|
+
color: #d4d4d4;
|
|
425
|
+
font-size: 13px;
|
|
426
|
+
text-align: left;
|
|
427
|
+
cursor: pointer;
|
|
428
|
+
transition: background 0.2s;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.context-menu-item:hover:not(:disabled) {
|
|
432
|
+
background: #3c3c3c;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.context-menu-item:disabled {
|
|
436
|
+
color: #858585;
|
|
437
|
+
cursor: not-allowed;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* Scrollbar styling */
|
|
441
|
+
.terminal-output::-webkit-scrollbar {
|
|
442
|
+
width: 8px;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.terminal-output::-webkit-scrollbar-track {
|
|
446
|
+
background: #1e1e1e;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.terminal-output::-webkit-scrollbar-thumb {
|
|
450
|
+
background: #515151;
|
|
451
|
+
border-radius: 4px;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.terminal-output::-webkit-scrollbar-thumb:hover {
|
|
455
|
+
background: #686868;
|
|
456
|
+
}
|
|
457
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Praxis Svelte Components
|
|
3
|
+
*
|
|
4
|
+
* Exports types and interfaces for Svelte components in Praxis/RuneBook applications.
|
|
5
|
+
*
|
|
6
|
+
* Note: Svelte components (.svelte files) must be imported directly in Svelte applications.
|
|
7
|
+
* This module provides TypeScript types for component props.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```svelte
|
|
11
|
+
* <script>
|
|
12
|
+
* import { TerminalNode } from '@plures/praxis/components/TerminalNode.svelte';
|
|
13
|
+
* </script>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { TerminalAdapter } from '../runtime/terminal-adapter.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Props for TerminalNode Svelte component
|
|
21
|
+
*/
|
|
22
|
+
export interface TerminalNodeProps {
|
|
23
|
+
/** Terminal adapter instance (required) */
|
|
24
|
+
adapter: TerminalAdapter;
|
|
25
|
+
/** X position on canvas */
|
|
26
|
+
x?: number;
|
|
27
|
+
/** Y position on canvas */
|
|
28
|
+
y?: number;
|
|
29
|
+
/** Component width in pixels */
|
|
30
|
+
width?: number;
|
|
31
|
+
/** Component height in pixels */
|
|
32
|
+
height?: number;
|
|
33
|
+
/** Enable drag to move */
|
|
34
|
+
draggable?: boolean;
|
|
35
|
+
/** Enable resize handle */
|
|
36
|
+
resizable?: boolean;
|
|
37
|
+
/** Show context menu */
|
|
38
|
+
showContextMenu?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Re-export TerminalAdapter for convenience
|
|
43
|
+
*/
|
|
44
|
+
export type { TerminalAdapter } from '../runtime/terminal-adapter.js';
|
|
45
|
+
export { createTerminalAdapter } from '../runtime/terminal-adapter.js';
|
|
46
|
+
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actors System
|
|
3
|
+
*
|
|
4
|
+
* Actors are effectful units that:
|
|
5
|
+
* - Observe Praxis logic state
|
|
6
|
+
* - Perform side effects (network I/O, database operations, timers, etc.)
|
|
7
|
+
* - Feed new events/facts back into the engine
|
|
8
|
+
*
|
|
9
|
+
* Actors provide the bridge between pure logic and the effectful world.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { LogicEngine } from "./engine.js";
|
|
13
|
+
import type { PraxisEvent, PraxisState } from "./protocol.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Actor interface
|
|
17
|
+
*
|
|
18
|
+
* An actor observes state changes and can:
|
|
19
|
+
* - React to state changes (onStateChange)
|
|
20
|
+
* - Perform initialization (onStart)
|
|
21
|
+
* - Perform cleanup (onStop)
|
|
22
|
+
*/
|
|
23
|
+
export interface Actor<TContext = unknown> {
|
|
24
|
+
/** Unique identifier for the actor */
|
|
25
|
+
id: string;
|
|
26
|
+
/** Human-readable description */
|
|
27
|
+
description: string;
|
|
28
|
+
/** Called when the actor is started */
|
|
29
|
+
onStart?: (engine: LogicEngine<TContext>) => void | Promise<void>;
|
|
30
|
+
/** Called when state changes */
|
|
31
|
+
onStateChange?: (
|
|
32
|
+
state: Readonly<PraxisState & { context: TContext }>,
|
|
33
|
+
engine: LogicEngine<TContext>
|
|
34
|
+
) => void | Promise<void>;
|
|
35
|
+
/** Called when the actor is stopped */
|
|
36
|
+
onStop?: () => void | Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Actor manager
|
|
41
|
+
*
|
|
42
|
+
* Manages the lifecycle of actors and coordinates their interaction with the engine.
|
|
43
|
+
*/
|
|
44
|
+
export class ActorManager<TContext = unknown> {
|
|
45
|
+
private actors = new Map<string, Actor<TContext>>();
|
|
46
|
+
private activeActors = new Set<string>();
|
|
47
|
+
private engine: LogicEngine<TContext> | null = null;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Register an actor
|
|
51
|
+
*/
|
|
52
|
+
register(actor: Actor<TContext>): void {
|
|
53
|
+
if (this.actors.has(actor.id)) {
|
|
54
|
+
throw new Error(`Actor with id "${actor.id}" already registered`);
|
|
55
|
+
}
|
|
56
|
+
this.actors.set(actor.id, actor);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Unregister an actor
|
|
61
|
+
*/
|
|
62
|
+
unregister(actorId: string): void {
|
|
63
|
+
if (this.activeActors.has(actorId)) {
|
|
64
|
+
throw new Error(`Cannot unregister active actor "${actorId}". Stop it first.`);
|
|
65
|
+
}
|
|
66
|
+
this.actors.delete(actorId);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Attach the actor manager to an engine
|
|
71
|
+
*/
|
|
72
|
+
attachEngine(engine: LogicEngine<TContext>): void {
|
|
73
|
+
this.engine = engine;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Start an actor
|
|
78
|
+
*/
|
|
79
|
+
async start(actorId: string): Promise<void> {
|
|
80
|
+
const actor = this.actors.get(actorId);
|
|
81
|
+
if (!actor) {
|
|
82
|
+
throw new Error(`Actor "${actorId}" not found`);
|
|
83
|
+
}
|
|
84
|
+
if (this.activeActors.has(actorId)) {
|
|
85
|
+
throw new Error(`Actor "${actorId}" is already started`);
|
|
86
|
+
}
|
|
87
|
+
if (!this.engine) {
|
|
88
|
+
throw new Error("Actor manager not attached to an engine");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.activeActors.add(actorId);
|
|
92
|
+
if (actor.onStart) {
|
|
93
|
+
await actor.onStart(this.engine);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Stop an actor
|
|
99
|
+
*/
|
|
100
|
+
async stop(actorId: string): Promise<void> {
|
|
101
|
+
const actor = this.actors.get(actorId);
|
|
102
|
+
if (!actor) {
|
|
103
|
+
throw new Error(`Actor "${actorId}" not found`);
|
|
104
|
+
}
|
|
105
|
+
if (!this.activeActors.has(actorId)) {
|
|
106
|
+
return; // Already stopped
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.activeActors.delete(actorId);
|
|
110
|
+
if (actor.onStop) {
|
|
111
|
+
await actor.onStop();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Start all registered actors
|
|
117
|
+
*/
|
|
118
|
+
async startAll(): Promise<void> {
|
|
119
|
+
const actorIds = Array.from(this.actors.keys());
|
|
120
|
+
for (const actorId of actorIds) {
|
|
121
|
+
if (!this.activeActors.has(actorId)) {
|
|
122
|
+
await this.start(actorId);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Stop all active actors
|
|
129
|
+
*/
|
|
130
|
+
async stopAll(): Promise<void> {
|
|
131
|
+
const activeIds = Array.from(this.activeActors);
|
|
132
|
+
for (const actorId of activeIds) {
|
|
133
|
+
await this.stop(actorId);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Notify active actors of a state change
|
|
139
|
+
*/
|
|
140
|
+
async notifyStateChange(state: Readonly<PraxisState & { context: TContext }>): Promise<void> {
|
|
141
|
+
if (!this.engine) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const promises: Promise<void>[] = [];
|
|
146
|
+
for (const actorId of this.activeActors) {
|
|
147
|
+
const actor = this.actors.get(actorId);
|
|
148
|
+
if (actor?.onStateChange) {
|
|
149
|
+
const result = actor.onStateChange(state, this.engine);
|
|
150
|
+
if (result instanceof Promise) {
|
|
151
|
+
promises.push(result);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
await Promise.all(promises);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get all registered actor IDs
|
|
160
|
+
*/
|
|
161
|
+
getActorIds(): string[] {
|
|
162
|
+
return Array.from(this.actors.keys());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get all active actor IDs
|
|
167
|
+
*/
|
|
168
|
+
getActiveActorIds(): string[] {
|
|
169
|
+
return Array.from(this.activeActors);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Check if an actor is active
|
|
174
|
+
*/
|
|
175
|
+
isActive(actorId: string): boolean {
|
|
176
|
+
return this.activeActors.has(actorId);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Helper to create a simple actor that dispatches events on a timer
|
|
182
|
+
*/
|
|
183
|
+
export function createTimerActor<TContext = unknown>(
|
|
184
|
+
id: string,
|
|
185
|
+
intervalMs: number,
|
|
186
|
+
createEvent: () => PraxisEvent
|
|
187
|
+
): Actor<TContext> {
|
|
188
|
+
let timerId: NodeJS.Timeout | null = null;
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
id,
|
|
192
|
+
description: `Timer actor (${intervalMs}ms) - ${id}`,
|
|
193
|
+
onStart: (engine) => {
|
|
194
|
+
timerId = setInterval(() => {
|
|
195
|
+
engine.step([createEvent()]);
|
|
196
|
+
}, intervalMs);
|
|
197
|
+
},
|
|
198
|
+
onStop: () => {
|
|
199
|
+
if (timerId) {
|
|
200
|
+
clearInterval(timerId);
|
|
201
|
+
timerId = null;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|