create-electro 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/LICENSE +21 -0
- package/README.md +24 -0
- package/bin/create-electro.mjs +9 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +10 -0
- package/package.json +54 -0
- package/template/monorepo/README.md +19 -0
- package/template/monorepo/electro.config.ts +6 -0
- package/template/monorepo/package.json +26 -0
- package/template/monorepo/pnpm-workspace.yaml +3 -0
- package/template/monorepo/runtime/electro-env.d.ts +90 -0
- package/template/monorepo/runtime/package.json +15 -0
- package/template/monorepo/runtime/runtime.config.ts +5 -0
- package/template/monorepo/runtime/src/main.ts +25 -0
- package/template/monorepo/runtime/src/modules/app.module.ts +35 -0
- package/template/monorepo/runtime/src/modules/app.shell.service.ts +54 -0
- package/template/monorepo/runtime/src/modules/app.view.ts +22 -0
- package/template/monorepo/runtime/src/modules/app.window.ts +54 -0
- package/template/monorepo/runtime/src/modules/notes/notes.module.ts +8 -0
- package/template/monorepo/runtime/src/modules/notes/notes.service.ts +55 -0
- package/template/monorepo/runtime/tsconfig.json +11 -0
- package/template/monorepo/views/main/electro-env.d.ts +51 -0
- package/template/monorepo/views/main/index.html +12 -0
- package/template/monorepo/views/main/package.json +17 -0
- package/template/monorepo/views/main/src/app.css +1254 -0
- package/template/monorepo/views/main/src/app.tsx +464 -0
- package/template/monorepo/views/main/src/icon.svg +11 -0
- package/template/monorepo/views/main/src/main.tsx +12 -0
- package/template/monorepo/views/main/tsconfig.json +9 -0
- package/template/monorepo/views/main/view.config.ts +8 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { bridge } from "@electrojs/renderer";
|
|
2
|
+
import { type KeyboardEvent, useEffect, useRef, useState } from "react";
|
|
3
|
+
import "./app.css";
|
|
4
|
+
|
|
5
|
+
type Note = {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly title: string;
|
|
8
|
+
readonly content: string;
|
|
9
|
+
readonly createdAt: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type ShellState = {
|
|
13
|
+
readonly platform: string;
|
|
14
|
+
readonly isMaximized: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type ViewId = "dashboard" | "notes";
|
|
18
|
+
|
|
19
|
+
const features = [
|
|
20
|
+
{
|
|
21
|
+
icon: "grid-view",
|
|
22
|
+
title: "Module System",
|
|
23
|
+
detail: "Organize your app into modules with explicit boundaries, imports, exports, and lifecycle hooks.",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
icon: "plug-socket",
|
|
27
|
+
title: "Typed Bridge",
|
|
28
|
+
detail: "Auto-generated typed IPC between main and renderer. Queries, commands, and signals — all type-safe.",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
icon: "tree-01",
|
|
32
|
+
title: "Dependency Injection",
|
|
33
|
+
detail: "Synchronous inject() with hierarchical scoping. No constructors, no service locator.",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
icon: "clock-01",
|
|
37
|
+
title: "Lifecycle-Aware",
|
|
38
|
+
detail: "onInit, onReady, onShutdown, onDispose — predictable resource management across your entire app.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
icon: "computer",
|
|
42
|
+
title: "Window & View System",
|
|
43
|
+
detail: "Declarative window management with per-view access control and source binding.",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
icon: "source-code-square",
|
|
47
|
+
title: "Code Generation",
|
|
48
|
+
detail: "Bridge types, preload scripts, and registry metadata — generated from your source code.",
|
|
49
|
+
},
|
|
50
|
+
] as const;
|
|
51
|
+
|
|
52
|
+
function formatDate(value: string): string {
|
|
53
|
+
return new Date(value).toLocaleDateString(undefined, {
|
|
54
|
+
month: "short",
|
|
55
|
+
day: "numeric",
|
|
56
|
+
hour: "2-digit",
|
|
57
|
+
minute: "2-digit",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function App() {
|
|
62
|
+
const [view, setView] = useState<ViewId>("dashboard");
|
|
63
|
+
const [notes, setNotes] = useState<Note[]>([]);
|
|
64
|
+
const [title, setTitle] = useState("");
|
|
65
|
+
const [content, setContent] = useState("");
|
|
66
|
+
const [shellState, setShellState] = useState<ShellState | null>(null);
|
|
67
|
+
const titleRef = useRef<HTMLInputElement>(null);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
void Promise.all([bridge.notes.getNotes(), bridge.app.getShellState()]).then(([loadedNotes, nextShellState]) => {
|
|
71
|
+
setNotes(loadedNotes);
|
|
72
|
+
setShellState(nextShellState);
|
|
73
|
+
});
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
async function handleCreate() {
|
|
77
|
+
const nextTitle = title.trim();
|
|
78
|
+
const nextContent = content.trim();
|
|
79
|
+
|
|
80
|
+
if (!nextTitle) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const note = await bridge.notes.createNote(nextTitle, nextContent);
|
|
85
|
+
setNotes((current) => [note, ...current]);
|
|
86
|
+
setTitle("");
|
|
87
|
+
setContent("");
|
|
88
|
+
titleRef.current?.focus();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function handleDelete(id: string) {
|
|
92
|
+
const removed = await bridge.notes.deleteNote(id);
|
|
93
|
+
if (removed) {
|
|
94
|
+
setNotes((current) => current.filter((note) => note.id !== id));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function handleToggleMaximize() {
|
|
99
|
+
const nextState = await bridge.app.toggleMaximizeWindow();
|
|
100
|
+
setShellState(nextState);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function handleOpenDocumentation() {
|
|
104
|
+
await bridge.app.openDocumentation();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function handleKeyDown(event: KeyboardEvent<HTMLElement>) {
|
|
108
|
+
if (event.key === "Enter" && (event.metaKey || event.ctrlKey) && title.trim()) {
|
|
109
|
+
void handleCreate();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const isMacOS = shellState?.platform === "darwin";
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div className="desktop-root">
|
|
117
|
+
<div className="plasma-orb plasma-orb-1" aria-hidden="true" />
|
|
118
|
+
<div className="plasma-orb plasma-orb-2" aria-hidden="true" />
|
|
119
|
+
<div className="plasma-orb plasma-orb-3" aria-hidden="true" />
|
|
120
|
+
|
|
121
|
+
<div className="window-shell">
|
|
122
|
+
<header className="titlebar drag-region">
|
|
123
|
+
<div className="titlebar-leading">
|
|
124
|
+
{isMacOS ? (
|
|
125
|
+
<div className="window-controls window-controls-mac no-drag">
|
|
126
|
+
<button className="window-control mac-close" onClick={() => void bridge.app.closeWindow()} aria-label="Close window" />
|
|
127
|
+
<button className="window-control mac-minimize" onClick={() => void bridge.app.minimizeWindow()} aria-label="Minimize window" />
|
|
128
|
+
<button
|
|
129
|
+
className="window-control mac-maximize"
|
|
130
|
+
onClick={() => void handleToggleMaximize()}
|
|
131
|
+
aria-label={shellState?.isMaximized ? "Restore window" : "Maximize window"}
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
) : (
|
|
135
|
+
<div className="titlebar-brand no-drag">
|
|
136
|
+
<span className="titlebar-mark" />
|
|
137
|
+
<span className="titlebar-brand-text">ElectroJS</span>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div className="titlebar-center">
|
|
143
|
+
<span className="titlebar-project">demo-app</span>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div className="titlebar-actions no-drag">
|
|
147
|
+
{!isMacOS && (
|
|
148
|
+
<div className="window-controls">
|
|
149
|
+
<button
|
|
150
|
+
className="window-control window-control-button"
|
|
151
|
+
onClick={() => void bridge.app.minimizeWindow()}
|
|
152
|
+
aria-label="Minimize window"
|
|
153
|
+
>
|
|
154
|
+
<span className="control-line" />
|
|
155
|
+
</button>
|
|
156
|
+
<button
|
|
157
|
+
className="window-control window-control-button"
|
|
158
|
+
onClick={() => void handleToggleMaximize()}
|
|
159
|
+
aria-label="Toggle maximize"
|
|
160
|
+
>
|
|
161
|
+
<span className={shellState?.isMaximized ? "control-restore" : "control-square"} />
|
|
162
|
+
</button>
|
|
163
|
+
<button
|
|
164
|
+
className="window-control window-control-button window-control-close"
|
|
165
|
+
onClick={() => void bridge.app.closeWindow()}
|
|
166
|
+
aria-label="Close window"
|
|
167
|
+
>
|
|
168
|
+
<span className="control-close" />
|
|
169
|
+
</button>
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
173
|
+
</header>
|
|
174
|
+
|
|
175
|
+
<div className="app-body">
|
|
176
|
+
<aside className="sidebar">
|
|
177
|
+
<div className="sidebar-brand">
|
|
178
|
+
<svg className="sidebar-logo" width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
179
|
+
<defs>
|
|
180
|
+
<linearGradient id="g" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse">
|
|
181
|
+
<stop offset="0%" stop-color="#FBBF24" />
|
|
182
|
+
<stop offset="50%" stop-color="#F97316" />
|
|
183
|
+
<stop offset="100%" stop-color="#EF4444" />
|
|
184
|
+
</linearGradient>
|
|
185
|
+
</defs>
|
|
186
|
+
<rect width="32" height="32" rx="8" />
|
|
187
|
+
<path d="M9 8h14l-3 7h5L12 26l2-9H9z" fill="url(#g)" />
|
|
188
|
+
</svg>
|
|
189
|
+
|
|
190
|
+
<span className="sidebar-logo-text">ElectroJS</span>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<nav className="sidebar-nav" aria-label="App navigation">
|
|
194
|
+
<button className={view === "dashboard" ? "nav-item is-active" : "nav-item"} onClick={() => setView("dashboard")}>
|
|
195
|
+
<svg
|
|
196
|
+
className="nav-icon"
|
|
197
|
+
viewBox="0 0 24 24"
|
|
198
|
+
fill="none"
|
|
199
|
+
stroke="currentColor"
|
|
200
|
+
strokeWidth="1.5"
|
|
201
|
+
strokeLinecap="round"
|
|
202
|
+
strokeLinejoin="round"
|
|
203
|
+
>
|
|
204
|
+
<rect x="3" y="3" width="7" height="7" rx="1.5" />
|
|
205
|
+
<rect x="14" y="3" width="7" height="7" rx="1.5" />
|
|
206
|
+
<rect x="3" y="14" width="7" height="7" rx="1.5" />
|
|
207
|
+
<rect x="14" y="14" width="7" height="7" rx="1.5" />
|
|
208
|
+
</svg>
|
|
209
|
+
Dashboard
|
|
210
|
+
</button>
|
|
211
|
+
<button className={view === "notes" ? "nav-item is-active" : "nav-item"} onClick={() => setView("notes")}>
|
|
212
|
+
<svg
|
|
213
|
+
className="nav-icon"
|
|
214
|
+
viewBox="0 0 24 24"
|
|
215
|
+
fill="none"
|
|
216
|
+
stroke="currentColor"
|
|
217
|
+
strokeWidth="1.5"
|
|
218
|
+
strokeLinecap="round"
|
|
219
|
+
strokeLinejoin="round"
|
|
220
|
+
>
|
|
221
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" />
|
|
222
|
+
<path d="M14 2v6h6" />
|
|
223
|
+
<line x1="8" y1="13" x2="16" y2="13" />
|
|
224
|
+
<line x1="8" y1="17" x2="16" y2="17" />
|
|
225
|
+
</svg>
|
|
226
|
+
Notes
|
|
227
|
+
{notes.length > 0 && <span className="nav-badge">{notes.length}</span>}
|
|
228
|
+
</button>
|
|
229
|
+
</nav>
|
|
230
|
+
|
|
231
|
+
<div className="sidebar-footer">
|
|
232
|
+
<button className="docs-button" onClick={() => void handleOpenDocumentation()}>
|
|
233
|
+
<svg
|
|
234
|
+
className="docs-icon"
|
|
235
|
+
viewBox="0 0 24 24"
|
|
236
|
+
fill="none"
|
|
237
|
+
stroke="currentColor"
|
|
238
|
+
strokeWidth="1.5"
|
|
239
|
+
strokeLinecap="round"
|
|
240
|
+
strokeLinejoin="round"
|
|
241
|
+
>
|
|
242
|
+
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2Z" />
|
|
243
|
+
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7Z" />
|
|
244
|
+
</svg>
|
|
245
|
+
Documentation
|
|
246
|
+
<svg
|
|
247
|
+
className="docs-arrow"
|
|
248
|
+
viewBox="0 0 24 24"
|
|
249
|
+
fill="none"
|
|
250
|
+
stroke="currentColor"
|
|
251
|
+
strokeWidth="2"
|
|
252
|
+
strokeLinecap="round"
|
|
253
|
+
strokeLinejoin="round"
|
|
254
|
+
>
|
|
255
|
+
<path d="M7 17L17 7" />
|
|
256
|
+
<path d="M7 7h10v10" />
|
|
257
|
+
</svg>
|
|
258
|
+
</button>
|
|
259
|
+
|
|
260
|
+
<div className="sidebar-meta">
|
|
261
|
+
<span className="meta-dot" />
|
|
262
|
+
<span className="meta-text">Runtime connected</span>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</aside>
|
|
266
|
+
|
|
267
|
+
<main className="content" onKeyDown={handleKeyDown}>
|
|
268
|
+
{view === "dashboard" && (
|
|
269
|
+
<div className="dashboard">
|
|
270
|
+
<div className="hero">
|
|
271
|
+
<p className="hero-eyebrow">Desktop starter</p>
|
|
272
|
+
<h1 className="hero-title">
|
|
273
|
+
Build with <span className="gradient-text">ElectroJS</span>
|
|
274
|
+
</h1>
|
|
275
|
+
<p className="hero-tagline">Modules. Typed IPC. Dependency injection. Built for desktop.</p>
|
|
276
|
+
<div className="hero-actions">
|
|
277
|
+
<button className="action-primary" onClick={() => setView("notes")}>
|
|
278
|
+
Try the demo
|
|
279
|
+
<svg
|
|
280
|
+
className="action-icon"
|
|
281
|
+
viewBox="0 0 24 24"
|
|
282
|
+
fill="none"
|
|
283
|
+
stroke="currentColor"
|
|
284
|
+
strokeWidth="2"
|
|
285
|
+
strokeLinecap="round"
|
|
286
|
+
strokeLinejoin="round"
|
|
287
|
+
>
|
|
288
|
+
<path d="M5 12h14" />
|
|
289
|
+
<path d="m12 5 7 7-7 7" />
|
|
290
|
+
</svg>
|
|
291
|
+
</button>
|
|
292
|
+
<button className="action-secondary" onClick={() => void handleOpenDocumentation()}>
|
|
293
|
+
Read the docs
|
|
294
|
+
</button>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<section className="features">
|
|
299
|
+
<p className="section-label">Framework features</p>
|
|
300
|
+
<div className="feature-grid">
|
|
301
|
+
{features.map((feature) => (
|
|
302
|
+
<article key={feature.title} className="feature-card">
|
|
303
|
+
<i className={`feature-icon hgi-stroke hgi-${feature.icon}`} />
|
|
304
|
+
<h3 className="feature-title">{feature.title}</h3>
|
|
305
|
+
<p className="feature-detail">{feature.detail}</p>
|
|
306
|
+
</article>
|
|
307
|
+
))}
|
|
308
|
+
</div>
|
|
309
|
+
</section>
|
|
310
|
+
|
|
311
|
+
<section className="architecture">
|
|
312
|
+
<p className="section-label">This app demonstrates</p>
|
|
313
|
+
<div className="arch-grid">
|
|
314
|
+
<div className="arch-card">
|
|
315
|
+
<code className="arch-code">@Module</code>
|
|
316
|
+
<p className="arch-text">Root module composing providers, views, windows, and imports</p>
|
|
317
|
+
</div>
|
|
318
|
+
<div className="arch-card">
|
|
319
|
+
<code className="arch-code">@Window</code>
|
|
320
|
+
<p className="arch-text">Frameless shell with custom title bar and native controls</p>
|
|
321
|
+
</div>
|
|
322
|
+
<div className="arch-card">
|
|
323
|
+
<code className="arch-code">@query · @command</code>
|
|
324
|
+
<p className="arch-text">Typed bridge methods for reading and writing runtime state</p>
|
|
325
|
+
</div>
|
|
326
|
+
<div className="arch-card">
|
|
327
|
+
<code className="arch-code">@View</code>
|
|
328
|
+
<p className="arch-text">Explicit access control declaring which bridge methods are available</p>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
</section>
|
|
332
|
+
</div>
|
|
333
|
+
)}
|
|
334
|
+
|
|
335
|
+
{view === "notes" && (
|
|
336
|
+
<div className="notes-view">
|
|
337
|
+
<div className="notes-header">
|
|
338
|
+
<div>
|
|
339
|
+
<p className="section-label">Live runtime demo</p>
|
|
340
|
+
<h2 className="view-title">Scratchpad</h2>
|
|
341
|
+
<p className="view-description">
|
|
342
|
+
Create and delete notes through the typed bridge. Every action crosses from the renderer into a runtime-owned
|
|
343
|
+
service.
|
|
344
|
+
</p>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<div className="notes-layout">
|
|
349
|
+
<div className="composer">
|
|
350
|
+
<div className="field">
|
|
351
|
+
<label className="field-label" htmlFor="note-title">
|
|
352
|
+
Title
|
|
353
|
+
</label>
|
|
354
|
+
<input
|
|
355
|
+
ref={titleRef}
|
|
356
|
+
id="note-title"
|
|
357
|
+
className="field-input"
|
|
358
|
+
placeholder="Note title..."
|
|
359
|
+
value={title}
|
|
360
|
+
onChange={(event) => setTitle(event.target.value)}
|
|
361
|
+
/>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<div className="field">
|
|
365
|
+
<label className="field-label" htmlFor="note-content">
|
|
366
|
+
Content
|
|
367
|
+
</label>
|
|
368
|
+
<textarea
|
|
369
|
+
id="note-content"
|
|
370
|
+
className="field-input field-textarea"
|
|
371
|
+
placeholder="Write something..."
|
|
372
|
+
value={content}
|
|
373
|
+
onChange={(event) => setContent(event.target.value)}
|
|
374
|
+
/>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<button className="create-button" onClick={() => void handleCreate()} disabled={!title.trim()}>
|
|
378
|
+
Create note
|
|
379
|
+
<span className="button-shortcut">{isMacOS ? "Cmd" : "Ctrl"} + Enter</span>
|
|
380
|
+
</button>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
<div className="notes-feed">
|
|
384
|
+
<div className="feed-header">
|
|
385
|
+
<span className="feed-title">Notes</span>
|
|
386
|
+
<span className="feed-count">{notes.length}</span>
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
<div className="feed-body">
|
|
390
|
+
{notes.length === 0 ? (
|
|
391
|
+
<div className="empty-state">
|
|
392
|
+
<div className="empty-icon-wrap">
|
|
393
|
+
<svg
|
|
394
|
+
className="empty-icon"
|
|
395
|
+
viewBox="0 0 24 24"
|
|
396
|
+
fill="none"
|
|
397
|
+
stroke="currentColor"
|
|
398
|
+
strokeWidth="1.5"
|
|
399
|
+
strokeLinecap="round"
|
|
400
|
+
strokeLinejoin="round"
|
|
401
|
+
>
|
|
402
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" />
|
|
403
|
+
<path d="M14 2v6h6" />
|
|
404
|
+
</svg>
|
|
405
|
+
</div>
|
|
406
|
+
<p className="empty-title">No notes yet</p>
|
|
407
|
+
<p className="empty-copy">Create your first note to verify the runtime bridge is live.</p>
|
|
408
|
+
</div>
|
|
409
|
+
) : (
|
|
410
|
+
<div className="note-list">
|
|
411
|
+
{notes.map((note) => (
|
|
412
|
+
<article key={note.id} className="note-card">
|
|
413
|
+
<div className="note-body">
|
|
414
|
+
<h3 className="note-title">{note.title}</h3>
|
|
415
|
+
{note.content && <p className="note-content">{note.content}</p>}
|
|
416
|
+
<time className="note-time">{formatDate(note.createdAt)}</time>
|
|
417
|
+
</div>
|
|
418
|
+
<button className="note-delete" onClick={() => void handleDelete(note.id)} aria-label="Delete note">
|
|
419
|
+
<svg
|
|
420
|
+
viewBox="0 0 24 24"
|
|
421
|
+
fill="none"
|
|
422
|
+
stroke="currentColor"
|
|
423
|
+
strokeWidth="1.5"
|
|
424
|
+
strokeLinecap="round"
|
|
425
|
+
strokeLinejoin="round"
|
|
426
|
+
>
|
|
427
|
+
<path d="M3 6h18" />
|
|
428
|
+
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
|
|
429
|
+
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
|
|
430
|
+
</svg>
|
|
431
|
+
</button>
|
|
432
|
+
</article>
|
|
433
|
+
))}
|
|
434
|
+
</div>
|
|
435
|
+
)}
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
|
|
439
|
+
<div className="bridge-info">
|
|
440
|
+
<p className="section-label">Bridge methods used</p>
|
|
441
|
+
<div className="bridge-methods">
|
|
442
|
+
<code className="bridge-method">
|
|
443
|
+
<span className="method-type">query</span>
|
|
444
|
+
bridge.notes.getNotes()
|
|
445
|
+
</code>
|
|
446
|
+
<code className="bridge-method">
|
|
447
|
+
<span className="method-type">command</span>
|
|
448
|
+
bridge.notes.createNote()
|
|
449
|
+
</code>
|
|
450
|
+
<code className="bridge-method">
|
|
451
|
+
<span className="method-type">command</span>
|
|
452
|
+
bridge.notes.deleteNote()
|
|
453
|
+
</code>
|
|
454
|
+
</div>
|
|
455
|
+
</div>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
)}
|
|
459
|
+
</main>
|
|
460
|
+
</div>
|
|
461
|
+
</div>
|
|
462
|
+
</div>
|
|
463
|
+
);
|
|
464
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="g" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0%" stop-color="#FBBF24"/>
|
|
5
|
+
<stop offset="50%" stop-color="#F97316"/>
|
|
6
|
+
<stop offset="100%" stop-color="#EF4444"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
<rect width="32" height="32" rx="8" />
|
|
10
|
+
<path d="M9 8h14l-3 7h5L12 26l2-9H9z" fill="url(#g)"/>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ElectroRenderer } from "@electrojs/renderer";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import ReactDOM from "react-dom/client";
|
|
4
|
+
import { App } from "./app";
|
|
5
|
+
|
|
6
|
+
await ElectroRenderer.initialize(() => {
|
|
7
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
8
|
+
<React.StrictMode>
|
|
9
|
+
<App />
|
|
10
|
+
</React.StrictMode>,
|
|
11
|
+
);
|
|
12
|
+
});
|