@portel/photon 1.26.0 → 1.27.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/dist/auto-ui/beam/routes/api-daemon.d.ts +1 -0
- package/dist/auto-ui/beam/routes/api-daemon.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-daemon.js +35 -1
- package/dist/auto-ui/beam/routes/api-daemon.js.map +1 -1
- package/dist/beam-form.bundle.js +41 -1
- package/dist/beam-form.bundle.js.map +2 -2
- package/dist/beam.bundle.js +1661 -252
- package/dist/beam.bundle.js.map +4 -4
- package/dist/cli/commands/daemon.d.ts.map +1 -1
- package/dist/cli/commands/daemon.js +157 -0
- package/dist/cli/commands/daemon.js.map +1 -1
- package/dist/daemon/client.d.ts +1 -0
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +110 -23
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/in-process-bridge.d.ts +29 -0
- package/dist/daemon/in-process-bridge.d.ts.map +1 -0
- package/dist/daemon/in-process-bridge.js +26 -0
- package/dist/daemon/in-process-bridge.js.map +1 -0
- package/dist/daemon/manager.d.ts +103 -1
- package/dist/daemon/manager.d.ts.map +1 -1
- package/dist/daemon/manager.js +313 -92
- package/dist/daemon/manager.js.map +1 -1
- package/dist/daemon/protocol.d.ts +1 -1
- package/dist/daemon/protocol.d.ts.map +1 -1
- package/dist/daemon/protocol.js +1 -0
- package/dist/daemon/protocol.js.map +1 -1
- package/dist/daemon/server.js +832 -37
- package/dist/daemon/server.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +11 -0
- package/dist/loader.js.map +1 -1
- package/package.json +1 -1
- package/templates/cloudflare/worker.ts.template +94 -22
package/dist/beam.bundle.js
CHANGED
|
@@ -32944,6 +32944,21 @@ function formatLabel(name2) {
|
|
|
32944
32944
|
spaced = spaced.replace(/[_-]+/g, " ");
|
|
32945
32945
|
let result = spaced.split(/\s+/).map((word) => {
|
|
32946
32946
|
if (!word) return "";
|
|
32947
|
+
const lower = word.toLowerCase();
|
|
32948
|
+
const mixedCase = {
|
|
32949
|
+
oauth: "OAuth",
|
|
32950
|
+
whatsapp: "WhatsApp",
|
|
32951
|
+
github: "GitHub",
|
|
32952
|
+
gitlab: "GitLab",
|
|
32953
|
+
bitbucket: "Bitbucket",
|
|
32954
|
+
macos: "macOS",
|
|
32955
|
+
ios: "iOS",
|
|
32956
|
+
ipad: "iPad",
|
|
32957
|
+
iphone: "iPhone",
|
|
32958
|
+
npm: "npm",
|
|
32959
|
+
npx: "npx"
|
|
32960
|
+
};
|
|
32961
|
+
if (lower in mixedCase) return mixedCase[lower];
|
|
32947
32962
|
const upper = word.toUpperCase();
|
|
32948
32963
|
if ([
|
|
32949
32964
|
"AI",
|
|
@@ -32972,7 +32987,32 @@ function formatLabel(name2) {
|
|
|
32972
32987
|
"OS",
|
|
32973
32988
|
"DB",
|
|
32974
32989
|
"IO",
|
|
32975
|
-
"GIT"
|
|
32990
|
+
"GIT",
|
|
32991
|
+
"CPU",
|
|
32992
|
+
"GPU",
|
|
32993
|
+
"RAM",
|
|
32994
|
+
"RPC",
|
|
32995
|
+
"TCP",
|
|
32996
|
+
"UDP",
|
|
32997
|
+
"DNS",
|
|
32998
|
+
"JWT",
|
|
32999
|
+
"TLS",
|
|
33000
|
+
"SSL",
|
|
33001
|
+
"CDN",
|
|
33002
|
+
"SVG",
|
|
33003
|
+
"PNG",
|
|
33004
|
+
"JPG",
|
|
33005
|
+
"PDF",
|
|
33006
|
+
"YAML",
|
|
33007
|
+
"TOML",
|
|
33008
|
+
"CSV",
|
|
33009
|
+
"TSV",
|
|
33010
|
+
"UUID",
|
|
33011
|
+
"CRUD",
|
|
33012
|
+
"REST",
|
|
33013
|
+
"GRPC",
|
|
33014
|
+
"MIME",
|
|
33015
|
+
"PATH"
|
|
32976
33016
|
].includes(upper)) {
|
|
32977
33017
|
if (upper === "IDS") return "IDs";
|
|
32978
33018
|
return upper;
|
|
@@ -35738,11 +35778,15 @@ var BeamApp = class extends i4 {
|
|
|
35738
35778
|
.hasPath=${!!this._selectedPhoton?.path}
|
|
35739
35779
|
@tab-change=${(e8) => {
|
|
35740
35780
|
const tab = e8.detail.tab;
|
|
35781
|
+
const prevTab = this._mainTab;
|
|
35741
35782
|
this._mainTab = tab;
|
|
35742
35783
|
if (tab === "methods") {
|
|
35743
|
-
|
|
35744
|
-
|
|
35745
|
-
|
|
35784
|
+
const fromOverlay = prevTab === "log" || prevTab === "help";
|
|
35785
|
+
if (!fromOverlay) {
|
|
35786
|
+
this._closeSecondPanel();
|
|
35787
|
+
this._selectedMethod = null;
|
|
35788
|
+
this._view = "list";
|
|
35789
|
+
}
|
|
35746
35790
|
this._updateRoute();
|
|
35747
35791
|
return;
|
|
35748
35792
|
}
|
|
@@ -35788,9 +35832,13 @@ var BeamApp = class extends i4 {
|
|
|
35788
35832
|
this._view = "mcp-app";
|
|
35789
35833
|
}
|
|
35790
35834
|
if (tab === "app" && this._selectedPhoton?.isApp && this._selectedPhoton?.appEntry) {
|
|
35791
|
-
|
|
35792
|
-
this._view
|
|
35793
|
-
|
|
35835
|
+
const appEntry = this._selectedPhoton.appEntry;
|
|
35836
|
+
const alreadyMounted = this._selectedMethod?.name === appEntry.name && this._view === "form" && this._lastResult != null;
|
|
35837
|
+
if (!alreadyMounted) {
|
|
35838
|
+
this._selectedMethod = appEntry;
|
|
35839
|
+
this._view = "form";
|
|
35840
|
+
this._maybeAutoInvoke(appEntry);
|
|
35841
|
+
}
|
|
35794
35842
|
}
|
|
35795
35843
|
}}
|
|
35796
35844
|
@toggle-focus=${() => this._toggleFocusMode()}
|
|
@@ -35893,12 +35941,35 @@ var BeamApp = class extends i4 {
|
|
|
35893
35941
|
${this._selectedPhoton && !this._selectedMethod && this._mainTab === "methods" && this._view !== "studio" ? b2`<div class="main-toolbar">
|
|
35894
35942
|
<div style="flex: 1; min-width: 0;">${this._renderPhotonToolbar()}</div>
|
|
35895
35943
|
</div>` : ""}
|
|
35896
|
-
${
|
|
35897
|
-
|
|
35898
|
-
|
|
35899
|
-
|
|
35900
|
-
|
|
35901
|
-
|
|
35944
|
+
${(() => {
|
|
35945
|
+
const sel = this._selectedPhoton;
|
|
35946
|
+
if (!sel) return this._renderContent();
|
|
35947
|
+
if (this._mainTab === "settings") {
|
|
35948
|
+
return this._renderSettingsView();
|
|
35949
|
+
}
|
|
35950
|
+
const isOverlay = this._mainTab === "log" || this._mainTab === "help";
|
|
35951
|
+
const isAppPhoton = !!sel.isApp || !!(sel.isExternalMCP && sel.hasMcpApp);
|
|
35952
|
+
const showsAppBento = this._mainTab === "methods" && !this._selectedMethod && isAppPhoton;
|
|
35953
|
+
return b2`
|
|
35954
|
+
<div class="tab-pane" ?hidden=${isOverlay || showsAppBento}>
|
|
35955
|
+
${this._renderContent()}
|
|
35956
|
+
</div>
|
|
35957
|
+
${isAppPhoton ? b2`<div class="tab-pane" ?hidden=${!showsAppBento}>
|
|
35958
|
+
${this._renderMethodsBentoOnly()}
|
|
35959
|
+
</div>` : ""}
|
|
35960
|
+
<div class="tab-pane" ?hidden=${this._mainTab !== "log"}>
|
|
35961
|
+
<photon-pulse
|
|
35962
|
+
.photonName=${sel.name}
|
|
35963
|
+
.availableMethods=${(sel.methods || []).map((m3) => m3.name)}
|
|
35964
|
+
.activity=${this._activityLog}
|
|
35965
|
+
@clear-activity=${() => this._activityLog = []}
|
|
35966
|
+
></photon-pulse>
|
|
35967
|
+
</div>
|
|
35968
|
+
<div class="tab-pane" ?hidden=${this._mainTab !== "help"}>
|
|
35969
|
+
${this._renderPhotonHelpView()}
|
|
35970
|
+
</div>
|
|
35971
|
+
`;
|
|
35972
|
+
})()}
|
|
35902
35973
|
</div>
|
|
35903
35974
|
</main>
|
|
35904
35975
|
|
|
@@ -37531,6 +37602,21 @@ ${photon.errorMessage || "Unknown error"}</pre
|
|
|
37531
37602
|
↓ Export
|
|
37532
37603
|
</button>
|
|
37533
37604
|
<button @click=${() => this._handleShareResult()}>Share</button>
|
|
37605
|
+
<button
|
|
37606
|
+
@click=${() => {
|
|
37607
|
+
const rv2 = this.shadowRoot?.querySelector("result-viewer");
|
|
37608
|
+
const el2 = rv2?.shadowRoot?.querySelector(".container") || rv2;
|
|
37609
|
+
if (!el2) return;
|
|
37610
|
+
if (document.fullscreenElement) {
|
|
37611
|
+
void document.exitFullscreen?.();
|
|
37612
|
+
} else {
|
|
37613
|
+
void el2.requestFullscreen?.();
|
|
37614
|
+
}
|
|
37615
|
+
}}
|
|
37616
|
+
title="Toggle fullscreen"
|
|
37617
|
+
>
|
|
37618
|
+
⛶ Fullscreen
|
|
37619
|
+
</button>
|
|
37534
37620
|
</div>
|
|
37535
37621
|
</div>` : ""}
|
|
37536
37622
|
${this._canvasActive ? b2`<canvas-renderer
|
|
@@ -38538,10 +38624,16 @@ ${photon.errorMessage || "Unknown error"}</pre
|
|
|
38538
38624
|
lines.push(method.description);
|
|
38539
38625
|
lines.push("");
|
|
38540
38626
|
}
|
|
38541
|
-
|
|
38627
|
+
const paramList = Array.isArray(method.params) ? method.params : method.params && typeof method.params === "object" && method.params.properties ? Object.entries(method.params.properties).map(([n5, prop]) => ({
|
|
38628
|
+
name: n5,
|
|
38629
|
+
type: prop?.type,
|
|
38630
|
+
description: prop?.description,
|
|
38631
|
+
required: Array.isArray(method.params.required) ? method.params.required.includes(n5) : false
|
|
38632
|
+
})) : [];
|
|
38633
|
+
if (paramList.length > 0) {
|
|
38542
38634
|
lines.push("| Name | Type | Required | Description |");
|
|
38543
38635
|
lines.push("|------|------|----------|-------------|");
|
|
38544
|
-
for (const param of
|
|
38636
|
+
for (const param of paramList) {
|
|
38545
38637
|
const required2 = param.required ? "\u2713" : "";
|
|
38546
38638
|
const desc = param.description || "-";
|
|
38547
38639
|
lines.push(`| \`${param.name}\` | ${param.type || "any"} | ${required2} | ${desc} |`);
|
|
@@ -38549,7 +38641,7 @@ ${photon.errorMessage || "Unknown error"}</pre
|
|
|
38549
38641
|
lines.push("");
|
|
38550
38642
|
}
|
|
38551
38643
|
if (!photon.internal) {
|
|
38552
|
-
const paramHints =
|
|
38644
|
+
const paramHints = paramList.filter((p5) => p5.required).map((p5) => `--${p5.name} <${p5.type || "value"}>`).join(" ");
|
|
38553
38645
|
const cliPfx = window.__PHOTON_SHELL_INIT ? name2 : `photon cli ${name2}`;
|
|
38554
38646
|
lines.push(`CLI: \`${cliPfx} ${method.name}${paramHints ? " " + paramHints : ""}\``);
|
|
38555
38647
|
lines.push("");
|
|
@@ -39324,7 +39416,7 @@ ${photon.errorMessage || "Unknown error"}</pre
|
|
|
39324
39416
|
<h2
|
|
39325
39417
|
style="font-family: var(--font-display); font-size: var(--text-xl); font-weight: 700; color: var(--t-primary); margin: 0 0 4px 0;"
|
|
39326
39418
|
>
|
|
39327
|
-
${photon.name} Settings
|
|
39419
|
+
${formatLabel(photon.name)} Settings
|
|
39328
39420
|
</h2>
|
|
39329
39421
|
<p style="color: var(--t-muted); font-size: var(--text-sm); margin: 0; line-height: 1.5;">
|
|
39330
39422
|
${activeTab === "setup" ? "What this photon needs to run. Applies to every instance; changes take effect on next load." : "How this photon behaves. Applies to the current instance only; changes are saved immediately."}
|
|
@@ -40082,6 +40174,21 @@ BeamApp.styles = [
|
|
|
40082
40174
|
flex-direction: column;
|
|
40083
40175
|
}
|
|
40084
40176
|
|
|
40177
|
+
/* Tab panes — siblings inside .main-content-scroll. The active pane
|
|
40178
|
+
is visible; non-active siblings stay mounted (DOM + component
|
|
40179
|
+
state) but are hidden via display: none, so an iframe survives a
|
|
40180
|
+
quick Pulse/Help peek and a half-typed form is still there when
|
|
40181
|
+
you return. */
|
|
40182
|
+
.tab-pane {
|
|
40183
|
+
display: flex;
|
|
40184
|
+
flex-direction: column;
|
|
40185
|
+
flex: 1;
|
|
40186
|
+
min-height: 0;
|
|
40187
|
+
}
|
|
40188
|
+
.tab-pane[hidden] {
|
|
40189
|
+
display: none !important;
|
|
40190
|
+
}
|
|
40191
|
+
|
|
40085
40192
|
/* Page transition when switching photons */
|
|
40086
40193
|
@keyframes page-enter {
|
|
40087
40194
|
from {
|
|
@@ -42083,7 +42190,7 @@ var BeamSidebar = class extends i4 {
|
|
|
42083
42190
|
@click=${() => this.dispatchEvent(new CustomEvent("home", { bubbles: true, composed: true }))}
|
|
42084
42191
|
title="Go home"
|
|
42085
42192
|
>
|
|
42086
|
-
Photon Beam
|
|
42193
|
+
<span class="logo-prefix">Photon </span>Beam
|
|
42087
42194
|
<span
|
|
42088
42195
|
class="status-indicator ${this.connected ? "connected" : this.reconnecting ? "reconnecting" : "disconnected"}"
|
|
42089
42196
|
title="${this.connected ? "Connected" : this.reconnecting ? "Reconnecting..." : "Disconnected"}"
|
|
@@ -42136,25 +42243,32 @@ var BeamSidebar = class extends i4 {
|
|
|
42136
42243
|
@click=${() => this._toggleFavoritesFilter()}
|
|
42137
42244
|
title="Show favorites only (f)"
|
|
42138
42245
|
aria-pressed="${this._showFavoritesOnly}"
|
|
42139
|
-
aria-label="Filter by favorites"
|
|
42246
|
+
aria-label="Filter by favorites${this._favorites.size > 0 ? ` (${this._favorites.size})` : ""}"
|
|
42140
42247
|
>
|
|
42141
|
-
|
|
42248
|
+
<span class="filter-icon-wrap">
|
|
42249
|
+
${starFilled}
|
|
42250
|
+
${this._favorites.size > 0 ? b2`<span class="filter-count-in-star" aria-hidden="true"
|
|
42251
|
+
>${this._favorites.size}</span
|
|
42252
|
+
>` : ""}
|
|
42253
|
+
</span>
|
|
42254
|
+
<span class="filter-btn-label">Favorites</span>
|
|
42142
42255
|
</button>
|
|
42143
42256
|
<button
|
|
42144
42257
|
class="filter-btn"
|
|
42145
42258
|
@click=${() => this.dispatchEvent(new CustomEvent("marketplace"))}
|
|
42146
42259
|
aria-label="Open marketplace"
|
|
42260
|
+
title="Marketplace"
|
|
42147
42261
|
>
|
|
42148
|
-
${marketplace} Marketplace
|
|
42262
|
+
${marketplace}<span class="filter-btn-label">Marketplace</span>
|
|
42149
42263
|
${this.updatesAvailable > 0 ? b2`<span class="update-badge">${this.updatesAvailable}</span>` : ""}
|
|
42150
42264
|
</button>
|
|
42151
42265
|
<button
|
|
42152
42266
|
class="filter-btn"
|
|
42153
42267
|
@click=${() => this.dispatchEvent(new CustomEvent("daemon"))}
|
|
42154
|
-
aria-label="Open
|
|
42155
|
-
title="
|
|
42268
|
+
aria-label="Open Pulse panel"
|
|
42269
|
+
title="Pulse: scheduled jobs, webhooks, and sessions"
|
|
42156
42270
|
>
|
|
42157
|
-
${activity}
|
|
42271
|
+
${activity}<span class="filter-btn-label">Pulse</span>
|
|
42158
42272
|
</button>
|
|
42159
42273
|
</div>
|
|
42160
42274
|
</div>
|
|
@@ -42507,7 +42621,7 @@ ${photon.path}` : ""}"
|
|
|
42507
42621
|
const tabs = [
|
|
42508
42622
|
...this.isApp ? [{ id: "app", label: "App", icon: appTabIcon }] : [],
|
|
42509
42623
|
{ id: "methods", label: "Methods", icon: methodsTabIcon },
|
|
42510
|
-
{ id: "log", label: "
|
|
42624
|
+
{ id: "log", label: "Pulse", icon: activity },
|
|
42511
42625
|
...this.hasSettings || this.hasSetup ? [{ id: "settings", label: "Settings", icon: settings }] : [],
|
|
42512
42626
|
...this.hasPath && !this.isExternalMCP ? [{ id: "source", label: "Source", icon: source }] : [],
|
|
42513
42627
|
{ id: "help", label: "Help", icon: docs }
|
|
@@ -42706,6 +42820,8 @@ BeamSidebar.styles = [
|
|
|
42706
42820
|
height: 100%;
|
|
42707
42821
|
color: var(--t-primary);
|
|
42708
42822
|
overflow: visible;
|
|
42823
|
+
container-type: inline-size;
|
|
42824
|
+
container-name: sidebar;
|
|
42709
42825
|
}
|
|
42710
42826
|
|
|
42711
42827
|
.sidebar-content {
|
|
@@ -42829,6 +42945,8 @@ BeamSidebar.styles = [
|
|
|
42829
42945
|
cursor: pointer;
|
|
42830
42946
|
user-select: none;
|
|
42831
42947
|
transition: opacity 0.15s;
|
|
42948
|
+
white-space: nowrap;
|
|
42949
|
+
min-width: 0;
|
|
42832
42950
|
}
|
|
42833
42951
|
|
|
42834
42952
|
.logo:hover {
|
|
@@ -43025,7 +43143,10 @@ BeamSidebar.styles = [
|
|
|
43025
43143
|
}
|
|
43026
43144
|
|
|
43027
43145
|
.photon-item {
|
|
43028
|
-
padding
|
|
43146
|
+
/* Right padding intentionally tight (4px) — the counts pill is the
|
|
43147
|
+
visual end of the row. Anything more reads as wasted space and
|
|
43148
|
+
steals room from the photon name. */
|
|
43149
|
+
padding: var(--space-sm) 4px var(--space-sm) var(--space-md);
|
|
43029
43150
|
margin-bottom: var(--space-xs);
|
|
43030
43151
|
border-radius: var(--radius-sm);
|
|
43031
43152
|
cursor: pointer;
|
|
@@ -43413,10 +43534,12 @@ BeamSidebar.styles = [
|
|
|
43413
43534
|
display: flex;
|
|
43414
43535
|
gap: var(--space-sm);
|
|
43415
43536
|
margin-top: var(--space-sm);
|
|
43537
|
+
min-width: 0;
|
|
43416
43538
|
}
|
|
43417
43539
|
|
|
43418
43540
|
.filter-btn {
|
|
43419
|
-
flex: 1;
|
|
43541
|
+
flex: 1 1 0;
|
|
43542
|
+
min-width: 0;
|
|
43420
43543
|
padding: var(--space-xs) var(--space-sm);
|
|
43421
43544
|
background: var(--bg-glass);
|
|
43422
43545
|
border: 1px solid var(--border-glass);
|
|
@@ -43428,9 +43551,16 @@ BeamSidebar.styles = [
|
|
|
43428
43551
|
justify-content: center;
|
|
43429
43552
|
gap: 4px;
|
|
43430
43553
|
font-size: var(--text-sm);
|
|
43554
|
+
white-space: nowrap;
|
|
43555
|
+
overflow: hidden;
|
|
43431
43556
|
transition: all 0.2s;
|
|
43432
43557
|
}
|
|
43433
43558
|
|
|
43559
|
+
.filter-btn-label {
|
|
43560
|
+
overflow: hidden;
|
|
43561
|
+
text-overflow: ellipsis;
|
|
43562
|
+
}
|
|
43563
|
+
|
|
43434
43564
|
.filter-btn:hover {
|
|
43435
43565
|
background: var(--bg-glass-strong);
|
|
43436
43566
|
}
|
|
@@ -43441,6 +43571,77 @@ BeamSidebar.styles = [
|
|
|
43441
43571
|
color: white;
|
|
43442
43572
|
}
|
|
43443
43573
|
|
|
43574
|
+
/* Favorites count is rendered centered inside the star itself —
|
|
43575
|
+
no separate badge, no horizontal space stolen from the label. */
|
|
43576
|
+
.filter-icon-wrap {
|
|
43577
|
+
position: relative;
|
|
43578
|
+
display: inline-flex;
|
|
43579
|
+
align-items: center;
|
|
43580
|
+
justify-content: center;
|
|
43581
|
+
flex-shrink: 0;
|
|
43582
|
+
color: var(--color-warning, #f59e0b);
|
|
43583
|
+
}
|
|
43584
|
+
|
|
43585
|
+
.filter-icon-wrap svg {
|
|
43586
|
+
width: 22px;
|
|
43587
|
+
height: 22px;
|
|
43588
|
+
}
|
|
43589
|
+
|
|
43590
|
+
.filter-count-in-star {
|
|
43591
|
+
position: absolute;
|
|
43592
|
+
top: 56%;
|
|
43593
|
+
left: 50%;
|
|
43594
|
+
transform: translate(-50%, -50%);
|
|
43595
|
+
font-size: 10px;
|
|
43596
|
+
font-weight: 800;
|
|
43597
|
+
line-height: 1;
|
|
43598
|
+
color: var(--bg-elevated, #0b1018);
|
|
43599
|
+
font-variant-numeric: tabular-nums;
|
|
43600
|
+
letter-spacing: -0.04em;
|
|
43601
|
+
pointer-events: none;
|
|
43602
|
+
}
|
|
43603
|
+
|
|
43604
|
+
.filter-btn.active .filter-icon-wrap {
|
|
43605
|
+
color: white;
|
|
43606
|
+
}
|
|
43607
|
+
.filter-btn.active .filter-count-in-star {
|
|
43608
|
+
color: var(--accent-primary);
|
|
43609
|
+
}
|
|
43610
|
+
|
|
43611
|
+
/* Responsive narrow mode: shrink the title to "Beam" and switch
|
|
43612
|
+
the three filter buttons to icon-only when the sidebar itself
|
|
43613
|
+
(not the viewport) is narrow. Container query watches :host's
|
|
43614
|
+
inline-size, so it fires whenever the user drags the sidebar
|
|
43615
|
+
below the threshold regardless of window width. */
|
|
43616
|
+
@container sidebar (max-width: 280px) {
|
|
43617
|
+
.logo-prefix {
|
|
43618
|
+
display: none;
|
|
43619
|
+
}
|
|
43620
|
+
.filter-btn {
|
|
43621
|
+
position: relative;
|
|
43622
|
+
padding: var(--space-xs);
|
|
43623
|
+
gap: 0;
|
|
43624
|
+
}
|
|
43625
|
+
.filter-btn .filter-btn-label {
|
|
43626
|
+
display: none;
|
|
43627
|
+
}
|
|
43628
|
+
.filter-btn .update-badge {
|
|
43629
|
+
position: absolute;
|
|
43630
|
+
top: 2px;
|
|
43631
|
+
right: 4px;
|
|
43632
|
+
min-width: 14px;
|
|
43633
|
+
height: 14px;
|
|
43634
|
+
padding: 0 4px;
|
|
43635
|
+
font-size: 9px;
|
|
43636
|
+
line-height: 1;
|
|
43637
|
+
display: inline-flex;
|
|
43638
|
+
align-items: center;
|
|
43639
|
+
justify-content: center;
|
|
43640
|
+
border: 1.5px solid var(--bg-elevated, #0b1018);
|
|
43641
|
+
border-radius: 999px;
|
|
43642
|
+
}
|
|
43643
|
+
}
|
|
43644
|
+
|
|
43444
43645
|
.settings-btn {
|
|
43445
43646
|
background: none;
|
|
43446
43647
|
border: none;
|
|
@@ -43778,7 +43979,7 @@ var MethodCard = class extends i4 {
|
|
|
43778
43979
|
_renderParamSignature() {
|
|
43779
43980
|
if (this.method.isTemplate) return "";
|
|
43780
43981
|
const props = this.method.params?.properties || {};
|
|
43781
|
-
const paramNames = Object.keys(props);
|
|
43982
|
+
const paramNames = Object.keys(props).map((p5) => formatLabel(p5));
|
|
43782
43983
|
if (paramNames.length === 0) return "";
|
|
43783
43984
|
if (paramNames.length <= 4) {
|
|
43784
43985
|
return b2`<span class="method-params method-params-trunc">(${paramNames.join(", ")}</span
|
|
@@ -46541,7 +46742,7 @@ var ActivityLog = class extends i4 {
|
|
|
46541
46742
|
}
|
|
46542
46743
|
render() {
|
|
46543
46744
|
const visible = this._filterActive && this.filter ? this.items.filter((i7) => i7.photonName === this.filter) : this.items;
|
|
46544
|
-
if (this.items.length === 0) return b2``;
|
|
46745
|
+
if (this.items.length === 0 && !this.fullscreen) return b2``;
|
|
46545
46746
|
const hasFilterableEntries = this.filter && this.items.some((i7) => i7.photonName === this.filter);
|
|
46546
46747
|
const typeIcon = (type) => {
|
|
46547
46748
|
switch (type) {
|
|
@@ -46579,22 +46780,24 @@ var ActivityLog = class extends i4 {
|
|
|
46579
46780
|
</div>
|
|
46580
46781
|
</div>
|
|
46581
46782
|
|
|
46582
|
-
${this._collapsed ? "" : b2
|
|
46583
|
-
|
|
46584
|
-
|
|
46783
|
+
${this._collapsed ? "" : visible.length === 0 ? b2`<div class="empty-hint" role="status">
|
|
46784
|
+
${this.items.length === 0 ? this.filter ? `No activity yet for ${this.filter}. Method calls and tool fires will appear here in real time.` : "No activity yet. Method calls and tool fires will appear here in real time." : `No activity yet for ${this.filter}. Toggle the filter off to see all photons.`}
|
|
46785
|
+
</div>` : b2`
|
|
46786
|
+
<ul class="log-list" role="log" aria-live="polite" aria-label="Activity log entries">
|
|
46787
|
+
${visible.map(
|
|
46585
46788
|
(item) => b2`
|
|
46586
|
-
|
|
46587
|
-
|
|
46588
|
-
|
|
46589
|
-
|
|
46590
|
-
|
|
46591
|
-
|
|
46592
|
-
|
|
46593
|
-
|
|
46594
|
-
|
|
46789
|
+
<li class="log-item type-${item.type}">
|
|
46790
|
+
<span class="type-icon" aria-hidden="true">${typeIcon(item.type)}</span>
|
|
46791
|
+
<span class="meta">${new Date(item.timestamp).toLocaleTimeString()}</span>
|
|
46792
|
+
<span class="content"
|
|
46793
|
+
><span class="visually-hidden">${item.type}: </span
|
|
46794
|
+
>${item.message}${item.durationMs != null ? b2`<span class="duration">${item.durationMs}ms</span>` : ""}${item.count && item.count > 1 ? b2`<span class="count">(×${item.count})</span>` : ""}</span
|
|
46795
|
+
>
|
|
46796
|
+
</li>
|
|
46797
|
+
`
|
|
46595
46798
|
)}
|
|
46596
|
-
|
|
46597
|
-
|
|
46799
|
+
</ul>
|
|
46800
|
+
`}
|
|
46598
46801
|
`;
|
|
46599
46802
|
}
|
|
46600
46803
|
_clear() {
|
|
@@ -46814,6 +47017,24 @@ ActivityLog.styles = [
|
|
|
46814
47017
|
transform: rotate(-90deg);
|
|
46815
47018
|
}
|
|
46816
47019
|
|
|
47020
|
+
.empty-hint {
|
|
47021
|
+
color: var(--t-muted);
|
|
47022
|
+
font-size: var(--text-sm);
|
|
47023
|
+
padding: var(--space-lg) var(--space-md);
|
|
47024
|
+
text-align: center;
|
|
47025
|
+
border: 1px dashed var(--border-glass);
|
|
47026
|
+
border-radius: var(--radius-sm);
|
|
47027
|
+
background: color-mix(in srgb, var(--bg-glass) 50%, transparent);
|
|
47028
|
+
}
|
|
47029
|
+
|
|
47030
|
+
:host([fullscreen]) .empty-hint {
|
|
47031
|
+
flex: 1;
|
|
47032
|
+
display: flex;
|
|
47033
|
+
align-items: center;
|
|
47034
|
+
justify-content: center;
|
|
47035
|
+
min-height: 120px;
|
|
47036
|
+
}
|
|
47037
|
+
|
|
46817
47038
|
/* ===== Responsive Design ===== */
|
|
46818
47039
|
@media (max-width: 768px) {
|
|
46819
47040
|
.log-item {
|
|
@@ -52288,7 +52509,7 @@ ${footerText || pageNum ? `<div class="slide-footer"><span>${footerText || ""}</
|
|
|
52288
52509
|
return this._columnPipes;
|
|
52289
52510
|
}
|
|
52290
52511
|
_applyPipe(value, pipe2, arg) {
|
|
52291
|
-
if (value === null || value === void 0) return "\
|
|
52512
|
+
if (value === null || value === void 0) return "\xB7";
|
|
52292
52513
|
switch (pipe2) {
|
|
52293
52514
|
case "currency":
|
|
52294
52515
|
return typeof value === "number" ? value.toLocaleString(void 0, { style: "currency", currency: arg || "USD" }) : String(value);
|
|
@@ -52323,9 +52544,9 @@ ${footerText || pageNum ? `<div class="slide-footer"><span>${footerText || ""}</
|
|
|
52323
52544
|
}
|
|
52324
52545
|
}
|
|
52325
52546
|
_formatCellValue(value, key, highlight = false) {
|
|
52326
|
-
if (value === null || value === void 0) return "\
|
|
52547
|
+
if (value === null || value === void 0) return "\xB7";
|
|
52327
52548
|
if (value === "" || typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0)
|
|
52328
|
-
return "\
|
|
52549
|
+
return "\xB7";
|
|
52329
52550
|
if (typeof value === "boolean") return value ? "\u2713" : "\u2717";
|
|
52330
52551
|
const pipes = this._getColumnPipes();
|
|
52331
52552
|
if (pipes.size > 0) {
|
|
@@ -52372,7 +52593,7 @@ ${footerText || pageNum ? `<div class="slide-footer"><span>${footerText || ""}</
|
|
|
52372
52593
|
>`;
|
|
52373
52594
|
}
|
|
52374
52595
|
if (Array.isArray(value)) {
|
|
52375
|
-
if (value.length === 0) return "\
|
|
52596
|
+
if (value.length === 0) return "\xB7";
|
|
52376
52597
|
if (value.every((v2) => typeof v2 !== "object" || v2 === null)) {
|
|
52377
52598
|
return b2`<span style="display:flex;flex-wrap:wrap;gap:3px;"
|
|
52378
52599
|
>${value.map(
|
|
@@ -54026,9 +54247,7 @@ ResultViewer.styles = [
|
|
|
54026
54247
|
width: 140px;
|
|
54027
54248
|
font-weight: 600;
|
|
54028
54249
|
color: var(--t-muted);
|
|
54029
|
-
text-transform: uppercase;
|
|
54030
54250
|
font-size: var(--text-xs);
|
|
54031
|
-
letter-spacing: 0.05em;
|
|
54032
54251
|
}
|
|
54033
54252
|
|
|
54034
54253
|
/* List Styles */
|
|
@@ -57355,7 +57574,7 @@ var MarketplaceView = class extends i4 {
|
|
|
57355
57574
|
All
|
|
57356
57575
|
<span class="count">${this._allItems.length}</span>
|
|
57357
57576
|
</button>
|
|
57358
|
-
${this._sources.filter((s5) => s5.enabled).map(
|
|
57577
|
+
${this._sources.filter((s5) => s5.enabled && !s5.name.endsWith(".photon.ts")).map(
|
|
57359
57578
|
(source3) => b2`
|
|
57360
57579
|
<button
|
|
57361
57580
|
class="filter-pill ${this._activeFilter === source3.name ? "active" : ""}"
|
|
@@ -58945,6 +59164,70 @@ function tilde(p5) {
|
|
|
58945
59164
|
if (parts.length <= 2) return p5;
|
|
58946
59165
|
return "\u2026/" + parts.slice(-2).join("/");
|
|
58947
59166
|
}
|
|
59167
|
+
function isImminent(ts) {
|
|
59168
|
+
if (!ts) return false;
|
|
59169
|
+
const delta = ts - Date.now();
|
|
59170
|
+
return delta >= 0 && delta < 6e4;
|
|
59171
|
+
}
|
|
59172
|
+
function groupByLocation(rows) {
|
|
59173
|
+
const map4 = /* @__PURE__ */ new Map();
|
|
59174
|
+
for (const r7 of rows) {
|
|
59175
|
+
const key = r7.workingDir ?? "";
|
|
59176
|
+
const list = map4.get(key);
|
|
59177
|
+
if (list) list.push(r7);
|
|
59178
|
+
else map4.set(key, [r7]);
|
|
59179
|
+
}
|
|
59180
|
+
return [...map4.entries()].sort(([a5], [b3]) => a5.localeCompare(b3));
|
|
59181
|
+
}
|
|
59182
|
+
var WEEKDAY_NAMES = [
|
|
59183
|
+
"Sunday",
|
|
59184
|
+
"Monday",
|
|
59185
|
+
"Tuesday",
|
|
59186
|
+
"Wednesday",
|
|
59187
|
+
"Thursday",
|
|
59188
|
+
"Friday",
|
|
59189
|
+
"Saturday"
|
|
59190
|
+
];
|
|
59191
|
+
function formatHour(h5) {
|
|
59192
|
+
return h5 === 0 ? "12am" : h5 < 12 ? `${h5}am` : h5 === 12 ? "12pm" : `${h5 - 12}pm`;
|
|
59193
|
+
}
|
|
59194
|
+
function describeWeekdays(spec) {
|
|
59195
|
+
if (spec === "*") return "every day";
|
|
59196
|
+
if (spec === "1-5") return "weekdays";
|
|
59197
|
+
if (spec === "0,6" || spec === "6,0") return "weekends";
|
|
59198
|
+
if (/^\d$/.test(spec)) {
|
|
59199
|
+
const n5 = parseInt(spec, 10);
|
|
59200
|
+
if (n5 >= 0 && n5 <= 6) return WEEKDAY_NAMES[n5];
|
|
59201
|
+
}
|
|
59202
|
+
return null;
|
|
59203
|
+
}
|
|
59204
|
+
function humanizeCron(cron) {
|
|
59205
|
+
const trimmed = cron.trim();
|
|
59206
|
+
if (trimmed === "* * * * *") return "Every minute";
|
|
59207
|
+
if (trimmed === "0 * * * *") return "Hourly";
|
|
59208
|
+
if (trimmed === "0 0 * * *") return "Daily at midnight";
|
|
59209
|
+
if (trimmed === "0 12 * * *") return "Daily at noon";
|
|
59210
|
+
if (trimmed === "0 0 1 * *") return "Monthly";
|
|
59211
|
+
let m3 = trimmed.match(/^(?:\*|0)\/(\d+) \* \* \* \*$/);
|
|
59212
|
+
if (m3) return `Every ${m3[1]} minutes`;
|
|
59213
|
+
m3 = trimmed.match(/^0 (?:\*|0)\/(\d+) \* \* \*$/);
|
|
59214
|
+
if (m3) return `Every ${m3[1]} hours`;
|
|
59215
|
+
m3 = trimmed.match(/^0 (\d{1,2}) \* \* \*$/);
|
|
59216
|
+
if (m3) {
|
|
59217
|
+
const h5 = parseInt(m3[1], 10);
|
|
59218
|
+
if (h5 >= 0 && h5 <= 23) return `Daily at ${formatHour(h5)}`;
|
|
59219
|
+
}
|
|
59220
|
+
m3 = trimmed.match(/^0 (\d{1,2}) \* \* ([0-9,-]+)$/);
|
|
59221
|
+
if (m3) {
|
|
59222
|
+
const h5 = parseInt(m3[1], 10);
|
|
59223
|
+
const dayLabel = describeWeekdays(m3[2]);
|
|
59224
|
+
if (h5 >= 0 && h5 <= 23 && dayLabel) {
|
|
59225
|
+
const cap = dayLabel[0].toUpperCase() + dayLabel.slice(1);
|
|
59226
|
+
return `${cap} at ${formatHour(h5)}`;
|
|
59227
|
+
}
|
|
59228
|
+
}
|
|
59229
|
+
return trimmed;
|
|
59230
|
+
}
|
|
58948
59231
|
var DaemonPanel = class extends i4 {
|
|
58949
59232
|
constructor() {
|
|
58950
59233
|
super(...arguments);
|
|
@@ -59035,157 +59318,267 @@ var DaemonPanel = class extends i4 {
|
|
|
59035
59318
|
_closeHistory() {
|
|
59036
59319
|
this._history = null;
|
|
59037
59320
|
}
|
|
59321
|
+
_sectionHead(title, count, sub) {
|
|
59322
|
+
return b2`
|
|
59323
|
+
<div class="section-head">
|
|
59324
|
+
<h2 class="section-title">
|
|
59325
|
+
${title} ${count > 0 ? b2`<span class="section-count">· ${count}</span>` : ""}
|
|
59326
|
+
</h2>
|
|
59327
|
+
${sub ? b2`<span class="section-sub">${sub}</span>` : ""}
|
|
59328
|
+
</div>
|
|
59329
|
+
`;
|
|
59330
|
+
}
|
|
59331
|
+
_entity(photon, method, dormant = false) {
|
|
59332
|
+
return b2`
|
|
59333
|
+
<span class="entity-row">
|
|
59334
|
+
<span class="status-dot ${dormant ? "dormant" : ""}"></span>
|
|
59335
|
+
<span class="entity"
|
|
59336
|
+
><span class="photon-part">${photon}</span><span class="sep">.</span
|
|
59337
|
+
><span class="method-part">${method}</span></span
|
|
59338
|
+
>
|
|
59339
|
+
</span>
|
|
59340
|
+
`;
|
|
59341
|
+
}
|
|
59342
|
+
_timingLine(cron, nextRun, lastRun) {
|
|
59343
|
+
const imminent = isImminent(nextRun);
|
|
59344
|
+
return b2`
|
|
59345
|
+
<span class="schedule" title="${cron}">${humanizeCron(cron)}</span>
|
|
59346
|
+
${nextRun ? b2`<span class="timing-sep">·</span>
|
|
59347
|
+
<span class="${imminent ? "imminent" : ""}">next ${formatWhen(nextRun)}</span>` : ""}
|
|
59348
|
+
${lastRun ? b2`<span class="timing-sep">·</span> <span>last ${formatWhen(lastRun)}</span>` : ""}
|
|
59349
|
+
`;
|
|
59350
|
+
}
|
|
59351
|
+
_groupHeader(loc, count, label) {
|
|
59352
|
+
return b2`
|
|
59353
|
+
<tr class="group-header">
|
|
59354
|
+
<td colspan="4">
|
|
59355
|
+
<span class="location-path" title="${loc || "(no working dir)"}">${tilde(loc)}</span>
|
|
59356
|
+
<span class="location-meta">${count} ${count === 1 ? label : label + "s"}</span>
|
|
59357
|
+
</td>
|
|
59358
|
+
</tr>
|
|
59359
|
+
`;
|
|
59360
|
+
}
|
|
59361
|
+
_iconBtn(opts) {
|
|
59362
|
+
const cls = opts.variant ? `icon-btn ${opts.variant}` : "icon-btn";
|
|
59363
|
+
return b2`
|
|
59364
|
+
<button
|
|
59365
|
+
class="${cls}"
|
|
59366
|
+
title="${opts.title}"
|
|
59367
|
+
aria-label="${opts.title}"
|
|
59368
|
+
@click=${opts.onClick}
|
|
59369
|
+
>
|
|
59370
|
+
${opts.icon === "history" ? b2`<svg
|
|
59371
|
+
viewBox="0 0 24 24"
|
|
59372
|
+
fill="none"
|
|
59373
|
+
stroke="currentColor"
|
|
59374
|
+
stroke-width="2"
|
|
59375
|
+
stroke-linecap="round"
|
|
59376
|
+
stroke-linejoin="round"
|
|
59377
|
+
aria-hidden="true"
|
|
59378
|
+
>
|
|
59379
|
+
<path d="M3 12a9 9 0 1 0 3-6.7L3 8" />
|
|
59380
|
+
<path d="M3 3v5h5" />
|
|
59381
|
+
<path d="M12 7v5l3 2" />
|
|
59382
|
+
</svg>` : opts.icon === "pause" ? b2`<svg
|
|
59383
|
+
viewBox="0 0 24 24"
|
|
59384
|
+
fill="none"
|
|
59385
|
+
stroke="currentColor"
|
|
59386
|
+
stroke-width="2"
|
|
59387
|
+
stroke-linecap="round"
|
|
59388
|
+
stroke-linejoin="round"
|
|
59389
|
+
aria-hidden="true"
|
|
59390
|
+
>
|
|
59391
|
+
<rect x="6" y="4" width="4" height="16" />
|
|
59392
|
+
<rect x="14" y="4" width="4" height="16" />
|
|
59393
|
+
</svg>` : opts.icon === "disable" ? b2`<svg
|
|
59394
|
+
viewBox="0 0 24 24"
|
|
59395
|
+
fill="none"
|
|
59396
|
+
stroke="currentColor"
|
|
59397
|
+
stroke-width="2"
|
|
59398
|
+
stroke-linecap="round"
|
|
59399
|
+
stroke-linejoin="round"
|
|
59400
|
+
aria-hidden="true"
|
|
59401
|
+
>
|
|
59402
|
+
<path d="M18.36 6.64a9 9 0 1 1-12.73 0" />
|
|
59403
|
+
<line x1="12" y1="2" x2="12" y2="12" />
|
|
59404
|
+
</svg>` : b2`<svg
|
|
59405
|
+
viewBox="0 0 24 24"
|
|
59406
|
+
fill="none"
|
|
59407
|
+
stroke="currentColor"
|
|
59408
|
+
stroke-width="2"
|
|
59409
|
+
stroke-linecap="round"
|
|
59410
|
+
stroke-linejoin="round"
|
|
59411
|
+
aria-hidden="true"
|
|
59412
|
+
>
|
|
59413
|
+
<polygon points="6 3 20 12 6 21" />
|
|
59414
|
+
</svg>`}
|
|
59415
|
+
</button>
|
|
59416
|
+
`;
|
|
59417
|
+
}
|
|
59418
|
+
_renderLoaded() {
|
|
59419
|
+
const rows = this._snap?.sessions ?? [];
|
|
59420
|
+
if (rows.length === 0) return "";
|
|
59421
|
+
const sorted = [...rows].sort((a5, b3) => a5.photon.localeCompare(b3.photon));
|
|
59422
|
+
const totalConn = sorted.reduce((acc, r7) => acc + r7.instanceCount, 0);
|
|
59423
|
+
const sub = `${totalConn} ${totalConn === 1 ? "connection" : "connections"}`;
|
|
59424
|
+
return b2`
|
|
59425
|
+
<section class="card">
|
|
59426
|
+
${this._sectionHead("Loaded photons", rows.length, sub)}
|
|
59427
|
+
<div class="chip-strip">
|
|
59428
|
+
${sorted.map(
|
|
59429
|
+
(r7) => b2`
|
|
59430
|
+
<span
|
|
59431
|
+
class="chip ${r7.instanceCount === 0 ? "idle" : ""}"
|
|
59432
|
+
title="${r7.workingDir ?? ""}"
|
|
59433
|
+
>
|
|
59434
|
+
<span class="chip-dot"></span>
|
|
59435
|
+
<span>${r7.photon}</span>
|
|
59436
|
+
${r7.instanceCount > 0 ? b2`<span class="conn-badge">${r7.instanceCount}</span>` : ""}
|
|
59437
|
+
</span>
|
|
59438
|
+
`
|
|
59439
|
+
)}
|
|
59440
|
+
</div>
|
|
59441
|
+
</section>
|
|
59442
|
+
`;
|
|
59443
|
+
}
|
|
59038
59444
|
_renderActive() {
|
|
59039
59445
|
const rows = this._snap?.active ?? [];
|
|
59446
|
+
const groups = groupByLocation(rows);
|
|
59447
|
+
for (const [, list] of groups) {
|
|
59448
|
+
list.sort((a5, b3) => (a5.nextRun ?? Infinity) - (b3.nextRun ?? Infinity));
|
|
59449
|
+
}
|
|
59040
59450
|
return b2`
|
|
59041
|
-
<
|
|
59042
|
-
|
|
59043
|
-
|
|
59044
|
-
|
|
59045
|
-
|
|
59046
|
-
|
|
59047
|
-
|
|
59048
|
-
|
|
59049
|
-
|
|
59050
|
-
|
|
59051
|
-
|
|
59052
|
-
|
|
59053
|
-
|
|
59054
|
-
|
|
59055
|
-
|
|
59056
|
-
|
|
59057
|
-
|
|
59058
|
-
|
|
59059
|
-
|
|
59060
|
-
|
|
59061
|
-
|
|
59062
|
-
|
|
59063
|
-
|
|
59064
|
-
|
|
59065
|
-
|
|
59066
|
-
|
|
59067
|
-
|
|
59068
|
-
|
|
59069
|
-
|
|
59070
|
-
|
|
59071
|
-
|
|
59072
|
-
|
|
59073
|
-
|
|
59074
|
-
|
|
59075
|
-
|
|
59076
|
-
|
|
59077
|
-
|
|
59078
|
-
|
|
59079
|
-
|
|
59080
|
-
|
|
59451
|
+
<section class="card">
|
|
59452
|
+
${this._sectionHead("Schedules", rows.length)}
|
|
59453
|
+
${rows.length === 0 ? b2`<p class="empty-line">No active schedules.</p>` : b2`<div class="table-wrap">
|
|
59454
|
+
<table class="grouped">
|
|
59455
|
+
${groups.map(
|
|
59456
|
+
([loc, items]) => b2`
|
|
59457
|
+
<tbody>
|
|
59458
|
+
${this._groupHeader(loc, items.length, "schedule")}
|
|
59459
|
+
${items.map(
|
|
59460
|
+
(r7) => b2`
|
|
59461
|
+
<tr>
|
|
59462
|
+
<td class="entity-cell">${this._entity(r7.photon, r7.method)}</td>
|
|
59463
|
+
<td class="timing">
|
|
59464
|
+
${this._timingLine(r7.cron, r7.nextRun, r7.lastRun)}
|
|
59465
|
+
</td>
|
|
59466
|
+
<td class="runs">
|
|
59467
|
+
${r7.runCount.toLocaleString()}<span class="runs-label"
|
|
59468
|
+
>${r7.runCount === 1 ? "run" : "runs"}</span
|
|
59469
|
+
>
|
|
59470
|
+
</td>
|
|
59471
|
+
<td class="actions">
|
|
59472
|
+
<span class="icon-bar">
|
|
59473
|
+
${this._iconBtn({
|
|
59474
|
+
icon: "history",
|
|
59475
|
+
title: "History",
|
|
59476
|
+
onClick: () => this._openHistory(r7.photon, r7.method)
|
|
59477
|
+
})}
|
|
59478
|
+
${this._iconBtn({
|
|
59479
|
+
icon: "pause",
|
|
59480
|
+
title: "Pause",
|
|
59481
|
+
onClick: () => this._scheduleAction("pause", r7.photon, r7.method)
|
|
59482
|
+
})}
|
|
59483
|
+
${this._iconBtn({
|
|
59484
|
+
icon: "disable",
|
|
59485
|
+
title: "Disable",
|
|
59486
|
+
onClick: () => this._scheduleAction("disable", r7.photon, r7.method),
|
|
59487
|
+
variant: "danger"
|
|
59488
|
+
})}
|
|
59489
|
+
</span>
|
|
59490
|
+
</td>
|
|
59491
|
+
</tr>
|
|
59492
|
+
`
|
|
59493
|
+
)}
|
|
59494
|
+
</tbody>
|
|
59081
59495
|
`
|
|
59082
59496
|
)}
|
|
59083
|
-
|
|
59084
|
-
|
|
59085
|
-
</
|
|
59497
|
+
</table>
|
|
59498
|
+
</div>`}
|
|
59499
|
+
</section>
|
|
59086
59500
|
`;
|
|
59087
59501
|
}
|
|
59088
59502
|
_renderDeclared() {
|
|
59089
59503
|
const rows = (this._snap?.declared ?? []).filter((d5) => !d5.active);
|
|
59504
|
+
if (rows.length === 0) return "";
|
|
59505
|
+
const groups = groupByLocation(rows);
|
|
59090
59506
|
return b2`
|
|
59091
|
-
<
|
|
59092
|
-
|
|
59093
|
-
|
|
59094
|
-
|
|
59095
|
-
|
|
59096
|
-
|
|
59097
|
-
|
|
59098
|
-
|
|
59099
|
-
|
|
59100
|
-
|
|
59101
|
-
|
|
59102
|
-
|
|
59103
|
-
|
|
59104
|
-
|
|
59105
|
-
|
|
59106
|
-
|
|
59107
|
-
|
|
59108
|
-
|
|
59109
|
-
|
|
59110
|
-
|
|
59111
|
-
|
|
59112
|
-
|
|
59113
|
-
|
|
59114
|
-
|
|
59115
|
-
|
|
59116
|
-
|
|
59117
|
-
|
|
59118
|
-
|
|
59119
|
-
|
|
59120
|
-
|
|
59121
|
-
|
|
59122
|
-
|
|
59507
|
+
<section class="card">
|
|
59508
|
+
${this._sectionHead("Pending", rows.length)}
|
|
59509
|
+
<p class="section-hint"><code>@scheduled</code> methods discovered but not yet enrolled.</p>
|
|
59510
|
+
<div class="table-wrap">
|
|
59511
|
+
<table class="grouped">
|
|
59512
|
+
${groups.map(
|
|
59513
|
+
([loc, items]) => b2`
|
|
59514
|
+
<tbody>
|
|
59515
|
+
${this._groupHeader(loc, items.length, "pending")}
|
|
59516
|
+
${items.map(
|
|
59517
|
+
(r7) => b2`
|
|
59518
|
+
<tr>
|
|
59519
|
+
<td class="entity-cell">${this._entity(r7.photon, r7.method, true)}</td>
|
|
59520
|
+
<td class="timing">
|
|
59521
|
+
<span class="schedule" title="${r7.cron}">${humanizeCron(r7.cron)}</span>
|
|
59522
|
+
</td>
|
|
59523
|
+
<td></td>
|
|
59524
|
+
<td class="actions">
|
|
59525
|
+
<span class="icon-bar">
|
|
59526
|
+
${this._iconBtn({
|
|
59527
|
+
icon: "play",
|
|
59528
|
+
title: "Enable",
|
|
59529
|
+
onClick: () => this._scheduleAction("enable", r7.photon, r7.method),
|
|
59530
|
+
variant: "primary"
|
|
59531
|
+
})}
|
|
59532
|
+
</span>
|
|
59533
|
+
</td>
|
|
59534
|
+
</tr>
|
|
59535
|
+
`
|
|
59536
|
+
)}
|
|
59537
|
+
</tbody>
|
|
59538
|
+
`
|
|
59123
59539
|
)}
|
|
59124
|
-
</
|
|
59125
|
-
</
|
|
59126
|
-
</
|
|
59540
|
+
</table>
|
|
59541
|
+
</div>
|
|
59542
|
+
</section>
|
|
59127
59543
|
`;
|
|
59128
59544
|
}
|
|
59129
59545
|
_renderWebhooks() {
|
|
59130
59546
|
const rows = this._snap?.webhooks ?? [];
|
|
59547
|
+
if (rows.length === 0) return "";
|
|
59548
|
+
const groups = groupByLocation(rows);
|
|
59131
59549
|
return b2`
|
|
59132
|
-
<
|
|
59133
|
-
|
|
59134
|
-
<table>
|
|
59135
|
-
<
|
|
59136
|
-
|
|
59137
|
-
|
|
59138
|
-
|
|
59139
|
-
|
|
59140
|
-
|
|
59141
|
-
|
|
59142
|
-
|
|
59143
|
-
|
|
59144
|
-
|
|
59145
|
-
|
|
59146
|
-
|
|
59147
|
-
|
|
59148
|
-
|
|
59149
|
-
|
|
59150
|
-
|
|
59151
|
-
|
|
59152
|
-
|
|
59153
|
-
|
|
59154
|
-
|
|
59155
|
-
|
|
59156
|
-
|
|
59157
|
-
|
|
59158
|
-
|
|
59159
|
-
|
|
59160
|
-
}
|
|
59161
|
-
_renderSessions() {
|
|
59162
|
-
const rows = this._snap?.sessions ?? [];
|
|
59163
|
-
return b2`
|
|
59164
|
-
<h2>Active sessions (${rows.length})</h2>
|
|
59165
|
-
<div class="table-wrap">
|
|
59166
|
-
<table>
|
|
59167
|
-
<thead>
|
|
59168
|
-
<tr>
|
|
59169
|
-
<th>Location</th>
|
|
59170
|
-
<th>Photon</th>
|
|
59171
|
-
<th>Instances</th>
|
|
59172
|
-
</tr>
|
|
59173
|
-
</thead>
|
|
59174
|
-
<tbody>
|
|
59175
|
-
${rows.length === 0 ? b2`<tr class="empty">
|
|
59176
|
-
<td colspan="3">no loaded sessions</td>
|
|
59177
|
-
</tr>` : rows.map(
|
|
59178
|
-
(r7) => b2`
|
|
59179
|
-
<tr>
|
|
59180
|
-
<td>${tilde(r7.workingDir)}</td>
|
|
59181
|
-
<td>${r7.photon}</td>
|
|
59182
|
-
<td>${r7.instanceCount}</td>
|
|
59183
|
-
</tr>
|
|
59184
|
-
`
|
|
59550
|
+
<section class="card">
|
|
59551
|
+
${this._sectionHead("Webhooks", rows.length)}
|
|
59552
|
+
<div class="table-wrap">
|
|
59553
|
+
<table class="grouped">
|
|
59554
|
+
${groups.map(
|
|
59555
|
+
([loc, items]) => b2`
|
|
59556
|
+
<tbody>
|
|
59557
|
+
${this._groupHeader(loc, items.length, "route")}
|
|
59558
|
+
${items.map((r7) => {
|
|
59559
|
+
const m3 = r7.route.match(/^([A-Z]+)\s+(.+)$/);
|
|
59560
|
+
const verb = m3?.[1] ?? "";
|
|
59561
|
+
const path = m3?.[2] ?? r7.route;
|
|
59562
|
+
return b2`
|
|
59563
|
+
<tr>
|
|
59564
|
+
<td>
|
|
59565
|
+
<span class="route-cell">
|
|
59566
|
+
<span class="route-photon">${r7.photon}</span>
|
|
59567
|
+
${verb ? b2`<span class="route-verb">${verb}</span>` : ""}
|
|
59568
|
+
<span class="route-path">${path}</span>
|
|
59569
|
+
<span class="route-arrow">→</span>
|
|
59570
|
+
<span class="route-handler">${r7.method}</span>
|
|
59571
|
+
</span>
|
|
59572
|
+
</td>
|
|
59573
|
+
</tr>
|
|
59574
|
+
`;
|
|
59575
|
+
})}
|
|
59576
|
+
</tbody>
|
|
59577
|
+
`
|
|
59185
59578
|
)}
|
|
59186
|
-
</
|
|
59187
|
-
</
|
|
59188
|
-
</
|
|
59579
|
+
</table>
|
|
59580
|
+
</div>
|
|
59581
|
+
</section>
|
|
59189
59582
|
`;
|
|
59190
59583
|
}
|
|
59191
59584
|
_renderHistoryDrawer() {
|
|
@@ -59230,21 +59623,19 @@ var DaemonPanel = class extends i4 {
|
|
|
59230
59623
|
}
|
|
59231
59624
|
render() {
|
|
59232
59625
|
return b2`
|
|
59233
|
-
<
|
|
59234
|
-
|
|
59235
|
-
Observability for scheduled work, webhook routes, and loaded sessions. Mirrors
|
|
59236
|
-
<code>photon ps</code>.
|
|
59237
|
-
</p>
|
|
59238
|
-
|
|
59239
|
-
<div class="refresh-bar">
|
|
59626
|
+
<div class="pulse-header">
|
|
59627
|
+
<h1>Pulse</h1>
|
|
59240
59628
|
<button class="refresh" @click=${() => this._refresh()} ?disabled=${this._loading}>
|
|
59241
59629
|
${this._loading ? "Refreshing\u2026" : "Refresh"}
|
|
59242
59630
|
</button>
|
|
59243
|
-
<span class="hint">auto-refresh every ${POLL_INTERVAL_MS / 1e3}s</span>
|
|
59244
59631
|
</div>
|
|
59632
|
+
<p class="hint">
|
|
59633
|
+
Heartbeat of the daemon: loaded photons, schedules, and webhooks. Mirrors
|
|
59634
|
+
<code>photon ps</code>. Auto-refresh every ${POLL_INTERVAL_MS / 1e3}s.
|
|
59635
|
+
</p>
|
|
59245
59636
|
|
|
59246
|
-
${this._error ? b2`<div class="error">${this._error}</div>` : ""} ${this.
|
|
59247
|
-
${this.
|
|
59637
|
+
${this._error ? b2`<div class="error">${this._error}</div>` : ""} ${this._renderLoaded()}
|
|
59638
|
+
${this._renderActive()} ${this._renderWebhooks()} ${this._renderDeclared()}
|
|
59248
59639
|
${this._renderHistoryDrawer()}
|
|
59249
59640
|
`;
|
|
59250
59641
|
}
|
|
@@ -59259,139 +59650,423 @@ DaemonPanel.styles = [
|
|
|
59259
59650
|
overflow-y: auto;
|
|
59260
59651
|
}
|
|
59261
59652
|
|
|
59653
|
+
.pulse-header {
|
|
59654
|
+
display: flex;
|
|
59655
|
+
align-items: baseline;
|
|
59656
|
+
justify-content: space-between;
|
|
59657
|
+
gap: var(--space-md);
|
|
59658
|
+
margin-bottom: var(--space-xs);
|
|
59659
|
+
}
|
|
59660
|
+
|
|
59262
59661
|
h1 {
|
|
59263
59662
|
font-size: 1.5rem;
|
|
59264
|
-
margin: 0
|
|
59265
|
-
|
|
59266
|
-
|
|
59267
|
-
h2 {
|
|
59268
|
-
font-size: 1rem;
|
|
59269
|
-
margin: var(--space-lg) 0 var(--space-xs) 0;
|
|
59270
|
-
color: var(--t-primary);
|
|
59663
|
+
margin: 0;
|
|
59664
|
+
color: var(--t-primary);
|
|
59271
59665
|
}
|
|
59272
59666
|
|
|
59273
59667
|
p.hint {
|
|
59274
59668
|
color: var(--t-muted);
|
|
59275
59669
|
font-size: 0.85rem;
|
|
59276
|
-
margin: 0 0 var(--space-
|
|
59277
|
-
}
|
|
59278
|
-
|
|
59279
|
-
.refresh-bar {
|
|
59280
|
-
display: flex;
|
|
59281
|
-
align-items: center;
|
|
59282
|
-
gap: var(--space-sm);
|
|
59283
|
-
margin-bottom: var(--space-md);
|
|
59670
|
+
margin: 0 0 var(--space-lg) 0;
|
|
59284
59671
|
}
|
|
59285
59672
|
|
|
59286
59673
|
button.refresh {
|
|
59287
|
-
background: var(--
|
|
59674
|
+
background: var(--bg-glass);
|
|
59288
59675
|
color: var(--t-primary);
|
|
59289
|
-
border: 1px solid var(--border
|
|
59676
|
+
border: 1px solid var(--border-glass);
|
|
59290
59677
|
padding: 4px 12px;
|
|
59291
|
-
border-radius:
|
|
59678
|
+
border-radius: var(--radius-sm);
|
|
59292
59679
|
cursor: pointer;
|
|
59680
|
+
font-size: 0.85rem;
|
|
59681
|
+
transition: all 0.15s ease;
|
|
59293
59682
|
}
|
|
59294
59683
|
button.refresh:hover {
|
|
59295
|
-
background: var(--
|
|
59684
|
+
background: var(--bg-glass-strong);
|
|
59685
|
+
border-color: color-mix(in srgb, var(--accent-primary) 38%, var(--border-glass));
|
|
59686
|
+
}
|
|
59687
|
+
button.refresh:disabled {
|
|
59688
|
+
opacity: 0.6;
|
|
59689
|
+
cursor: not-allowed;
|
|
59690
|
+
}
|
|
59691
|
+
|
|
59692
|
+
/* Section card */
|
|
59693
|
+
section.card {
|
|
59694
|
+
background: color-mix(in srgb, var(--bg-glass) 60%, transparent);
|
|
59695
|
+
border: 1px solid var(--border-glass);
|
|
59696
|
+
border-radius: var(--radius-md);
|
|
59697
|
+
padding: var(--space-md);
|
|
59698
|
+
margin-bottom: var(--space-md);
|
|
59699
|
+
}
|
|
59700
|
+
|
|
59701
|
+
.section-head {
|
|
59702
|
+
display: flex;
|
|
59703
|
+
align-items: baseline;
|
|
59704
|
+
justify-content: space-between;
|
|
59705
|
+
gap: var(--space-md);
|
|
59706
|
+
margin-bottom: var(--space-sm);
|
|
59707
|
+
}
|
|
59708
|
+
|
|
59709
|
+
.section-title {
|
|
59710
|
+
margin: 0;
|
|
59711
|
+
font-family: 'Azeret Mono', var(--font-mono);
|
|
59712
|
+
font-size: 0.72rem;
|
|
59713
|
+
text-transform: uppercase;
|
|
59714
|
+
letter-spacing: 0.12em;
|
|
59715
|
+
color: var(--t-primary);
|
|
59716
|
+
font-weight: 600;
|
|
59717
|
+
display: flex;
|
|
59718
|
+
align-items: baseline;
|
|
59719
|
+
gap: var(--space-xs);
|
|
59720
|
+
}
|
|
59721
|
+
|
|
59722
|
+
.section-count {
|
|
59723
|
+
color: var(--t-muted);
|
|
59724
|
+
font-size: 0.7rem;
|
|
59725
|
+
font-weight: 500;
|
|
59726
|
+
letter-spacing: 0.04em;
|
|
59727
|
+
}
|
|
59728
|
+
|
|
59729
|
+
.section-sub {
|
|
59730
|
+
color: var(--t-muted);
|
|
59731
|
+
font-size: 0.7rem;
|
|
59732
|
+
font-weight: 500;
|
|
59733
|
+
letter-spacing: 0.04em;
|
|
59734
|
+
}
|
|
59735
|
+
|
|
59736
|
+
.section-hint {
|
|
59737
|
+
color: var(--t-muted);
|
|
59738
|
+
font-size: 0.78rem;
|
|
59739
|
+
margin: 0 0 var(--space-sm) 0;
|
|
59740
|
+
}
|
|
59741
|
+
|
|
59742
|
+
.empty-line {
|
|
59743
|
+
color: var(--t-muted);
|
|
59744
|
+
font-size: 0.85rem;
|
|
59745
|
+
font-style: italic;
|
|
59746
|
+
margin: var(--space-xs) 0 0 0;
|
|
59296
59747
|
}
|
|
59297
59748
|
|
|
59749
|
+
/* Tables */
|
|
59298
59750
|
.table-wrap {
|
|
59299
59751
|
overflow-x: auto;
|
|
59300
|
-
border: 1px solid var(--border, #333);
|
|
59301
|
-
border-radius: 4px;
|
|
59302
59752
|
}
|
|
59303
59753
|
|
|
59304
|
-
table {
|
|
59754
|
+
table.grouped {
|
|
59305
59755
|
width: 100%;
|
|
59306
|
-
border-collapse:
|
|
59756
|
+
border-collapse: separate;
|
|
59757
|
+
border-spacing: 0;
|
|
59758
|
+
}
|
|
59759
|
+
|
|
59760
|
+
table.grouped td {
|
|
59761
|
+
padding: 6px 8px;
|
|
59762
|
+
border-bottom: 1px solid color-mix(in srgb, var(--border-glass) 50%, transparent);
|
|
59763
|
+
vertical-align: middle;
|
|
59307
59764
|
font-size: 0.85rem;
|
|
59308
59765
|
}
|
|
59309
59766
|
|
|
59310
|
-
|
|
59311
|
-
|
|
59312
|
-
padding: 6px 10px;
|
|
59313
|
-
text-align: left;
|
|
59314
|
-
border-bottom: 1px solid var(--border, #2a2a2a);
|
|
59767
|
+
table.grouped tbody:last-child tr:last-child td {
|
|
59768
|
+
border-bottom: none;
|
|
59315
59769
|
}
|
|
59316
59770
|
|
|
59317
|
-
|
|
59318
|
-
background: var(--
|
|
59771
|
+
table.grouped tbody tr:not(.group-header):hover td {
|
|
59772
|
+
background: color-mix(in srgb, var(--bg-glass) 60%, transparent);
|
|
59773
|
+
}
|
|
59774
|
+
|
|
59775
|
+
/* Group header row (location) — separated by spacing + thin top rule */
|
|
59776
|
+
tr.group-header td {
|
|
59777
|
+
padding: var(--space-xl) 8px var(--space-xs) 8px;
|
|
59778
|
+
background: transparent;
|
|
59779
|
+
border-bottom: none;
|
|
59780
|
+
border-top: 1px solid var(--border-glass);
|
|
59781
|
+
}
|
|
59782
|
+
table.grouped tbody:first-child tr.group-header td {
|
|
59783
|
+
padding-top: var(--space-2xs);
|
|
59784
|
+
border-top: none;
|
|
59785
|
+
}
|
|
59786
|
+
.location-path {
|
|
59787
|
+
font-family: 'Azeret Mono', var(--font-mono);
|
|
59788
|
+
font-size: 0.72rem;
|
|
59789
|
+
color: var(--t-primary);
|
|
59790
|
+
letter-spacing: 0.08em;
|
|
59791
|
+
text-transform: uppercase;
|
|
59319
59792
|
font-weight: 600;
|
|
59793
|
+
}
|
|
59794
|
+
.location-meta {
|
|
59320
59795
|
color: var(--t-muted);
|
|
59796
|
+
font-size: 0.7rem;
|
|
59797
|
+
margin-left: var(--space-sm);
|
|
59798
|
+
opacity: 0.7;
|
|
59799
|
+
text-transform: lowercase;
|
|
59800
|
+
font-weight: 400;
|
|
59801
|
+
letter-spacing: 0.02em;
|
|
59321
59802
|
}
|
|
59322
59803
|
|
|
59323
|
-
|
|
59324
|
-
|
|
59804
|
+
/* Entity cell — status dot + photon.method */
|
|
59805
|
+
td.entity-cell {
|
|
59806
|
+
white-space: nowrap;
|
|
59807
|
+
width: 1%;
|
|
59808
|
+
}
|
|
59809
|
+
.entity-row {
|
|
59810
|
+
display: inline-flex;
|
|
59811
|
+
align-items: center;
|
|
59812
|
+
gap: var(--space-xs);
|
|
59813
|
+
}
|
|
59814
|
+
.status-dot {
|
|
59815
|
+
width: 7px;
|
|
59816
|
+
height: 7px;
|
|
59817
|
+
border-radius: 50%;
|
|
59818
|
+
background: var(--color-success);
|
|
59819
|
+
box-shadow: 0 0 5px color-mix(in srgb, var(--color-success) 50%, transparent);
|
|
59820
|
+
flex-shrink: 0;
|
|
59821
|
+
}
|
|
59822
|
+
.status-dot.dormant {
|
|
59823
|
+
background: var(--t-muted);
|
|
59824
|
+
box-shadow: none;
|
|
59825
|
+
opacity: 0.45;
|
|
59826
|
+
}
|
|
59827
|
+
.entity {
|
|
59828
|
+
font-family: 'Azeret Mono', var(--font-mono);
|
|
59829
|
+
font-size: 0.85rem;
|
|
59830
|
+
white-space: nowrap;
|
|
59831
|
+
}
|
|
59832
|
+
.entity .photon-part {
|
|
59325
59833
|
color: var(--t-muted);
|
|
59326
|
-
|
|
59834
|
+
}
|
|
59835
|
+
.entity .sep {
|
|
59836
|
+
color: var(--t-muted);
|
|
59837
|
+
}
|
|
59838
|
+
.entity .method-part {
|
|
59839
|
+
color: var(--t-primary);
|
|
59840
|
+
font-weight: 600;
|
|
59327
59841
|
}
|
|
59328
59842
|
|
|
59329
|
-
|
|
59330
|
-
|
|
59331
|
-
|
|
59843
|
+
/* Timing line */
|
|
59844
|
+
td.timing {
|
|
59845
|
+
color: var(--t-muted);
|
|
59846
|
+
font-size: 0.8rem;
|
|
59847
|
+
font-variant-numeric: tabular-nums;
|
|
59848
|
+
}
|
|
59849
|
+
.timing .schedule {
|
|
59850
|
+
color: var(--t-primary);
|
|
59851
|
+
}
|
|
59852
|
+
.timing .timing-sep {
|
|
59853
|
+
margin: 0 var(--space-xs);
|
|
59854
|
+
opacity: 0.4;
|
|
59855
|
+
}
|
|
59856
|
+
.timing .imminent {
|
|
59857
|
+
color: var(--accent-primary);
|
|
59332
59858
|
}
|
|
59333
59859
|
|
|
59334
|
-
|
|
59335
|
-
|
|
59336
|
-
|
|
59337
|
-
|
|
59338
|
-
background: transparent;
|
|
59860
|
+
/* Runs */
|
|
59861
|
+
td.runs {
|
|
59862
|
+
text-align: right;
|
|
59863
|
+
font-variant-numeric: tabular-nums;
|
|
59339
59864
|
color: var(--t-primary);
|
|
59340
|
-
|
|
59341
|
-
|
|
59865
|
+
font-size: 0.82rem;
|
|
59866
|
+
white-space: nowrap;
|
|
59867
|
+
width: 1%;
|
|
59868
|
+
}
|
|
59869
|
+
.runs-label {
|
|
59870
|
+
color: var(--t-muted);
|
|
59871
|
+
margin-left: 4px;
|
|
59872
|
+
font-size: 0.72rem;
|
|
59873
|
+
}
|
|
59874
|
+
|
|
59875
|
+
/* Webhook route cell */
|
|
59876
|
+
.route-cell {
|
|
59877
|
+
font-family: 'Azeret Mono', var(--font-mono);
|
|
59878
|
+
font-size: 0.83rem;
|
|
59879
|
+
display: inline-flex;
|
|
59880
|
+
align-items: center;
|
|
59881
|
+
gap: var(--space-xs);
|
|
59882
|
+
white-space: nowrap;
|
|
59883
|
+
}
|
|
59884
|
+
.route-photon {
|
|
59885
|
+
color: var(--t-muted);
|
|
59886
|
+
}
|
|
59887
|
+
.route-verb {
|
|
59888
|
+
color: var(--accent-secondary);
|
|
59889
|
+
font-weight: 600;
|
|
59890
|
+
font-size: 0.7rem;
|
|
59891
|
+
padding: 1px 5px;
|
|
59892
|
+
border-radius: var(--radius-xs);
|
|
59893
|
+
background: color-mix(in srgb, var(--accent-secondary) 12%, transparent);
|
|
59894
|
+
letter-spacing: 0.04em;
|
|
59895
|
+
}
|
|
59896
|
+
.route-path {
|
|
59897
|
+
color: var(--t-primary);
|
|
59898
|
+
}
|
|
59899
|
+
.route-arrow {
|
|
59900
|
+
color: var(--t-muted);
|
|
59901
|
+
opacity: 0.5;
|
|
59902
|
+
margin: 0 4px;
|
|
59903
|
+
}
|
|
59904
|
+
.route-handler {
|
|
59905
|
+
color: var(--t-primary);
|
|
59906
|
+
font-weight: 600;
|
|
59342
59907
|
}
|
|
59343
59908
|
|
|
59344
|
-
|
|
59345
|
-
|
|
59909
|
+
/* Action icons */
|
|
59910
|
+
td.actions {
|
|
59911
|
+
text-align: right;
|
|
59912
|
+
white-space: nowrap;
|
|
59913
|
+
width: 1%;
|
|
59914
|
+
}
|
|
59915
|
+
.icon-bar {
|
|
59916
|
+
display: inline-flex;
|
|
59917
|
+
gap: 2px;
|
|
59918
|
+
justify-content: flex-end;
|
|
59919
|
+
opacity: 0.65;
|
|
59920
|
+
transition: opacity 0.15s ease;
|
|
59921
|
+
}
|
|
59922
|
+
tr:hover .icon-bar,
|
|
59923
|
+
.icon-bar:focus-within {
|
|
59924
|
+
opacity: 1;
|
|
59925
|
+
}
|
|
59926
|
+
.icon-btn svg {
|
|
59927
|
+
width: 14px;
|
|
59928
|
+
height: 14px;
|
|
59929
|
+
}
|
|
59930
|
+
.icon-btn {
|
|
59931
|
+
width: 26px;
|
|
59932
|
+
height: 26px;
|
|
59933
|
+
display: inline-flex;
|
|
59934
|
+
align-items: center;
|
|
59935
|
+
justify-content: center;
|
|
59936
|
+
background: transparent;
|
|
59937
|
+
border: 1px solid transparent;
|
|
59938
|
+
color: var(--t-muted);
|
|
59939
|
+
border-radius: var(--radius-xs);
|
|
59940
|
+
cursor: pointer;
|
|
59941
|
+
padding: 0;
|
|
59942
|
+
transition: all 0.15s ease;
|
|
59943
|
+
}
|
|
59944
|
+
.icon-btn:hover {
|
|
59945
|
+
background: var(--bg-glass-strong);
|
|
59946
|
+
color: var(--t-primary);
|
|
59947
|
+
border-color: var(--border-glass);
|
|
59948
|
+
}
|
|
59949
|
+
.icon-btn.danger:hover {
|
|
59950
|
+
color: var(--color-error);
|
|
59951
|
+
}
|
|
59952
|
+
.icon-btn.primary {
|
|
59953
|
+
background: color-mix(in srgb, var(--accent-primary) 16%, transparent);
|
|
59954
|
+
color: var(--accent-primary);
|
|
59955
|
+
border-color: color-mix(in srgb, var(--accent-primary) 30%, var(--border-glass));
|
|
59956
|
+
}
|
|
59957
|
+
.icon-btn.primary:hover {
|
|
59958
|
+
background: color-mix(in srgb, var(--accent-primary) 24%, transparent);
|
|
59959
|
+
}
|
|
59960
|
+
/* Loaded photons chip strip */
|
|
59961
|
+
.chip-strip {
|
|
59962
|
+
display: flex;
|
|
59963
|
+
flex-wrap: wrap;
|
|
59964
|
+
gap: var(--space-xs);
|
|
59965
|
+
}
|
|
59966
|
+
.chip {
|
|
59967
|
+
font-family: 'Azeret Mono', var(--font-mono);
|
|
59968
|
+
font-size: 0.78rem;
|
|
59969
|
+
background: color-mix(in srgb, var(--bg-glass) 70%, transparent);
|
|
59970
|
+
color: var(--t-primary);
|
|
59971
|
+
border: 1px solid var(--border-glass);
|
|
59972
|
+
border-radius: var(--radius-full);
|
|
59973
|
+
padding: 3px 10px 3px 8px;
|
|
59974
|
+
display: inline-flex;
|
|
59975
|
+
align-items: center;
|
|
59976
|
+
gap: 6px;
|
|
59977
|
+
}
|
|
59978
|
+
.chip.idle {
|
|
59979
|
+
color: var(--t-muted);
|
|
59980
|
+
}
|
|
59981
|
+
.chip-dot {
|
|
59982
|
+
width: 6px;
|
|
59983
|
+
height: 6px;
|
|
59984
|
+
border-radius: 50%;
|
|
59985
|
+
background: var(--color-success);
|
|
59986
|
+
box-shadow: 0 0 4px color-mix(in srgb, var(--color-success) 40%, transparent);
|
|
59987
|
+
}
|
|
59988
|
+
.chip.idle .chip-dot {
|
|
59989
|
+
background: var(--t-muted);
|
|
59990
|
+
opacity: 0.4;
|
|
59991
|
+
box-shadow: none;
|
|
59992
|
+
}
|
|
59993
|
+
.conn-badge {
|
|
59994
|
+
background: var(--accent-primary);
|
|
59995
|
+
color: var(--bg-app);
|
|
59996
|
+
font-weight: 700;
|
|
59997
|
+
font-size: 0.66rem;
|
|
59998
|
+
padding: 1px 7px;
|
|
59999
|
+
border-radius: var(--radius-full);
|
|
60000
|
+
letter-spacing: 0.02em;
|
|
59346
60001
|
}
|
|
59347
60002
|
|
|
60003
|
+
/* Status colors (history drawer) */
|
|
59348
60004
|
.status-success {
|
|
59349
|
-
color: var(--success
|
|
60005
|
+
color: var(--color-success);
|
|
59350
60006
|
}
|
|
59351
60007
|
.status-error {
|
|
59352
|
-
color: var(--
|
|
60008
|
+
color: var(--color-error);
|
|
59353
60009
|
}
|
|
59354
60010
|
.status-timeout {
|
|
59355
|
-
color: var(--warning
|
|
60011
|
+
color: var(--color-warning);
|
|
59356
60012
|
}
|
|
59357
60013
|
|
|
59358
60014
|
.error {
|
|
59359
|
-
background: var(--
|
|
59360
|
-
color: var(--
|
|
59361
|
-
|
|
59362
|
-
|
|
60015
|
+
background: color-mix(in srgb, var(--color-error) 12%, transparent);
|
|
60016
|
+
color: var(--color-error);
|
|
60017
|
+
border: 1px solid color-mix(in srgb, var(--color-error) 30%, transparent);
|
|
60018
|
+
padding: var(--space-sm) var(--space-md);
|
|
60019
|
+
border-radius: var(--radius-sm);
|
|
59363
60020
|
font-size: 0.85rem;
|
|
59364
60021
|
margin-bottom: var(--space-md);
|
|
59365
60022
|
}
|
|
59366
60023
|
|
|
60024
|
+
/* History drawer */
|
|
59367
60025
|
.drawer {
|
|
59368
60026
|
position: fixed;
|
|
59369
60027
|
top: 0;
|
|
59370
60028
|
right: 0;
|
|
59371
60029
|
bottom: 0;
|
|
59372
60030
|
width: min(520px, 100vw);
|
|
59373
|
-
background: var(--
|
|
59374
|
-
border-left: 1px solid var(--border
|
|
60031
|
+
background: var(--bg-elevated, #111);
|
|
60032
|
+
border-left: 1px solid var(--border-glass);
|
|
59375
60033
|
padding: var(--space-md);
|
|
59376
60034
|
overflow-y: auto;
|
|
59377
60035
|
z-index: 9999;
|
|
59378
60036
|
box-shadow: -4px 0 12px rgba(0, 0, 0, 0.4);
|
|
59379
60037
|
}
|
|
59380
|
-
|
|
59381
60038
|
.drawer-header {
|
|
59382
60039
|
display: flex;
|
|
59383
60040
|
justify-content: space-between;
|
|
59384
60041
|
align-items: center;
|
|
59385
60042
|
margin-bottom: var(--space-md);
|
|
59386
60043
|
}
|
|
59387
|
-
|
|
59388
60044
|
.drawer-header button {
|
|
59389
60045
|
background: transparent;
|
|
59390
|
-
border: 1px solid var(--border
|
|
60046
|
+
border: 1px solid var(--border-glass);
|
|
59391
60047
|
color: var(--t-primary);
|
|
59392
60048
|
padding: 4px 10px;
|
|
59393
60049
|
cursor: pointer;
|
|
59394
|
-
border-radius:
|
|
60050
|
+
border-radius: var(--radius-xs);
|
|
60051
|
+
}
|
|
60052
|
+
.drawer table {
|
|
60053
|
+
width: 100%;
|
|
60054
|
+
border-collapse: separate;
|
|
60055
|
+
border-spacing: 0;
|
|
60056
|
+
font-size: 0.85rem;
|
|
60057
|
+
}
|
|
60058
|
+
.drawer th,
|
|
60059
|
+
.drawer td {
|
|
60060
|
+
padding: 8px 10px;
|
|
60061
|
+
text-align: left;
|
|
60062
|
+
border-bottom: 1px solid var(--border-glass);
|
|
60063
|
+
}
|
|
60064
|
+
.drawer th {
|
|
60065
|
+
font-weight: 600;
|
|
60066
|
+
color: var(--t-muted);
|
|
60067
|
+
font-size: 0.75rem;
|
|
60068
|
+
text-transform: uppercase;
|
|
60069
|
+
letter-spacing: 0.05em;
|
|
59395
60070
|
}
|
|
59396
60071
|
`
|
|
59397
60072
|
];
|
|
@@ -59414,6 +60089,740 @@ DaemonPanel = __decorateClass([
|
|
|
59414
60089
|
t4("daemon-panel")
|
|
59415
60090
|
], DaemonPanel);
|
|
59416
60091
|
|
|
60092
|
+
// src/auto-ui/frontend/components/photon-pulse.ts
|
|
60093
|
+
init_lit();
|
|
60094
|
+
init_decorators();
|
|
60095
|
+
init_theme();
|
|
60096
|
+
var POLL_INTERVAL_MS2 = 5e3;
|
|
60097
|
+
var WEEKDAY_NAMES2 = [
|
|
60098
|
+
"Sunday",
|
|
60099
|
+
"Monday",
|
|
60100
|
+
"Tuesday",
|
|
60101
|
+
"Wednesday",
|
|
60102
|
+
"Thursday",
|
|
60103
|
+
"Friday",
|
|
60104
|
+
"Saturday"
|
|
60105
|
+
];
|
|
60106
|
+
function formatHour2(h5) {
|
|
60107
|
+
return h5 === 0 ? "12am" : h5 < 12 ? `${h5}am` : h5 === 12 ? "12pm" : `${h5 - 12}pm`;
|
|
60108
|
+
}
|
|
60109
|
+
function describeWeekdays2(spec) {
|
|
60110
|
+
if (spec === "*") return "every day";
|
|
60111
|
+
if (spec === "1-5") return "weekdays";
|
|
60112
|
+
if (spec === "0,6" || spec === "6,0") return "weekends";
|
|
60113
|
+
if (/^\d$/.test(spec)) {
|
|
60114
|
+
const n5 = parseInt(spec, 10);
|
|
60115
|
+
if (n5 >= 0 && n5 <= 6) return WEEKDAY_NAMES2[n5];
|
|
60116
|
+
}
|
|
60117
|
+
return null;
|
|
60118
|
+
}
|
|
60119
|
+
function humanizeCron2(cron) {
|
|
60120
|
+
const trimmed = cron.trim();
|
|
60121
|
+
if (trimmed === "* * * * *") return "Every minute";
|
|
60122
|
+
if (trimmed === "0 * * * *") return "Hourly";
|
|
60123
|
+
if (trimmed === "0 0 * * *") return "Daily at midnight";
|
|
60124
|
+
if (trimmed === "0 12 * * *") return "Daily at noon";
|
|
60125
|
+
if (trimmed === "0 0 1 * *") return "Monthly";
|
|
60126
|
+
let m3 = trimmed.match(/^(?:\*|0)\/(\d+) \* \* \* \*$/);
|
|
60127
|
+
if (m3) return `Every ${m3[1]} minutes`;
|
|
60128
|
+
m3 = trimmed.match(/^0 (?:\*|0)\/(\d+) \* \* \*$/);
|
|
60129
|
+
if (m3) return `Every ${m3[1]} hours`;
|
|
60130
|
+
m3 = trimmed.match(/^0 (\d{1,2}) \* \* \*$/);
|
|
60131
|
+
if (m3) {
|
|
60132
|
+
const h5 = parseInt(m3[1], 10);
|
|
60133
|
+
if (h5 >= 0 && h5 <= 23) return `Daily at ${formatHour2(h5)}`;
|
|
60134
|
+
}
|
|
60135
|
+
m3 = trimmed.match(/^0 (\d{1,2}) \* \* ([0-9,-]+)$/);
|
|
60136
|
+
if (m3) {
|
|
60137
|
+
const h5 = parseInt(m3[1], 10);
|
|
60138
|
+
const dayLabel = describeWeekdays2(m3[2]);
|
|
60139
|
+
if (h5 >= 0 && h5 <= 23 && dayLabel) {
|
|
60140
|
+
const cap = dayLabel[0].toUpperCase() + dayLabel.slice(1);
|
|
60141
|
+
return `${cap} at ${formatHour2(h5)}`;
|
|
60142
|
+
}
|
|
60143
|
+
}
|
|
60144
|
+
return trimmed;
|
|
60145
|
+
}
|
|
60146
|
+
var CRON_PRESETS = [
|
|
60147
|
+
{ label: "Every minute", cron: "* * * * *" },
|
|
60148
|
+
{ label: "Every 5 min", cron: "*/5 * * * *" },
|
|
60149
|
+
{ label: "Hourly", cron: "0 * * * *" },
|
|
60150
|
+
{ label: "Daily 9am", cron: "0 9 * * *" },
|
|
60151
|
+
{ label: "Weekday mornings", cron: "0 9 * * 1-5" },
|
|
60152
|
+
{ label: "Weekly Mon", cron: "0 9 * * 1" },
|
|
60153
|
+
{ label: "Monthly 1st", cron: "0 0 1 * *" }
|
|
60154
|
+
];
|
|
60155
|
+
function isCronShapeValid(cron) {
|
|
60156
|
+
const trimmed = cron.trim();
|
|
60157
|
+
if (!trimmed) return false;
|
|
60158
|
+
const parts = trimmed.split(/\s+/);
|
|
60159
|
+
if (parts.length !== 5) return false;
|
|
60160
|
+
return parts.every((p5) => /^[0-9*/,-]+$/.test(p5));
|
|
60161
|
+
}
|
|
60162
|
+
function formatWhen2(ts) {
|
|
60163
|
+
if (!ts) return "-";
|
|
60164
|
+
const delta = ts - Date.now();
|
|
60165
|
+
const abs = Math.abs(delta);
|
|
60166
|
+
const mins = Math.round(abs / 6e4);
|
|
60167
|
+
const hrs = Math.round(mins / 60);
|
|
60168
|
+
if (abs < 6e4) return delta < 0 ? "just now" : "in <1m";
|
|
60169
|
+
if (mins < 90) return delta < 0 ? `${mins}m ago` : `in ${mins}m`;
|
|
60170
|
+
if (hrs < 48) return delta < 0 ? `${hrs}h ago` : `in ${hrs}h`;
|
|
60171
|
+
return new Date(ts).toISOString().replace("T", " ").slice(0, 16);
|
|
60172
|
+
}
|
|
60173
|
+
var PhotonPulse = class extends i4 {
|
|
60174
|
+
constructor() {
|
|
60175
|
+
super(...arguments);
|
|
60176
|
+
this.photonName = "";
|
|
60177
|
+
this.availableMethods = [];
|
|
60178
|
+
this.activity = [];
|
|
60179
|
+
this._active = [];
|
|
60180
|
+
this._declared = [];
|
|
60181
|
+
this._error = null;
|
|
60182
|
+
this._showAddForm = false;
|
|
60183
|
+
this._formMethod = "";
|
|
60184
|
+
this._formCron = "0 9 * * *";
|
|
60185
|
+
this._formSubmitting = false;
|
|
60186
|
+
this._pollTimer = null;
|
|
60187
|
+
}
|
|
60188
|
+
connectedCallback() {
|
|
60189
|
+
super.connectedCallback();
|
|
60190
|
+
void this._refresh();
|
|
60191
|
+
this._pollTimer = setInterval(() => void this._refresh(), POLL_INTERVAL_MS2);
|
|
60192
|
+
}
|
|
60193
|
+
disconnectedCallback() {
|
|
60194
|
+
super.disconnectedCallback();
|
|
60195
|
+
if (this._pollTimer) {
|
|
60196
|
+
clearInterval(this._pollTimer);
|
|
60197
|
+
this._pollTimer = null;
|
|
60198
|
+
}
|
|
60199
|
+
}
|
|
60200
|
+
updated(changed) {
|
|
60201
|
+
if (changed.has("photonName")) {
|
|
60202
|
+
void this._refresh();
|
|
60203
|
+
}
|
|
60204
|
+
}
|
|
60205
|
+
async _refresh() {
|
|
60206
|
+
if (!this.photonName) return;
|
|
60207
|
+
try {
|
|
60208
|
+
const res = await fetch("/api/daemon/ps", { signal: AbortSignal.timeout(1e4) });
|
|
60209
|
+
if (!res.ok) {
|
|
60210
|
+
this._error = `HTTP ${res.status}`;
|
|
60211
|
+
return;
|
|
60212
|
+
}
|
|
60213
|
+
const snap = await res.json();
|
|
60214
|
+
this._active = (snap.active ?? []).filter((r7) => r7.photon === this.photonName);
|
|
60215
|
+
this._declared = (snap.declared ?? []).filter(
|
|
60216
|
+
(r7) => r7.photon === this.photonName && !r7.active
|
|
60217
|
+
);
|
|
60218
|
+
this._error = null;
|
|
60219
|
+
} catch (err) {
|
|
60220
|
+
this._error = err instanceof Error ? err.message : String(err);
|
|
60221
|
+
}
|
|
60222
|
+
}
|
|
60223
|
+
async _addManual() {
|
|
60224
|
+
const method = this._formMethod.trim();
|
|
60225
|
+
const cron = this._formCron.trim();
|
|
60226
|
+
if (!method) {
|
|
60227
|
+
showToast("Pick a method first", "error");
|
|
60228
|
+
return;
|
|
60229
|
+
}
|
|
60230
|
+
if (!cron) {
|
|
60231
|
+
showToast("Cron expression is required", "error");
|
|
60232
|
+
return;
|
|
60233
|
+
}
|
|
60234
|
+
this._formSubmitting = true;
|
|
60235
|
+
try {
|
|
60236
|
+
const res = await fetch("/api/daemon/schedules/add", {
|
|
60237
|
+
method: "POST",
|
|
60238
|
+
headers: {
|
|
60239
|
+
"Content-Type": "application/json",
|
|
60240
|
+
"X-Photon-Request": "1"
|
|
60241
|
+
},
|
|
60242
|
+
body: JSON.stringify({ photon: this.photonName, method, cron }),
|
|
60243
|
+
signal: AbortSignal.timeout(1e4)
|
|
60244
|
+
});
|
|
60245
|
+
const body = await res.json().catch(() => ({}));
|
|
60246
|
+
if (!res.ok) {
|
|
60247
|
+
showToast(body.error || "Failed to add schedule", "error");
|
|
60248
|
+
return;
|
|
60249
|
+
}
|
|
60250
|
+
showToast(`Scheduled ${this.photonName}:${method}`, "success");
|
|
60251
|
+
this._showAddForm = false;
|
|
60252
|
+
this._formMethod = "";
|
|
60253
|
+
this._formCron = "0 9 * * *";
|
|
60254
|
+
await this._refresh();
|
|
60255
|
+
} catch (err) {
|
|
60256
|
+
showToast(err instanceof Error ? err.message : String(err), "error");
|
|
60257
|
+
} finally {
|
|
60258
|
+
this._formSubmitting = false;
|
|
60259
|
+
}
|
|
60260
|
+
}
|
|
60261
|
+
async _action(op, photon, method) {
|
|
60262
|
+
try {
|
|
60263
|
+
const res = await fetch(`/api/daemon/schedules/${op}`, {
|
|
60264
|
+
method: "POST",
|
|
60265
|
+
headers: {
|
|
60266
|
+
"Content-Type": "application/json",
|
|
60267
|
+
"X-Photon-Request": "1"
|
|
60268
|
+
},
|
|
60269
|
+
body: JSON.stringify({ photon, method }),
|
|
60270
|
+
signal: AbortSignal.timeout(1e4)
|
|
60271
|
+
});
|
|
60272
|
+
const body = await res.json().catch(() => ({}));
|
|
60273
|
+
if (!res.ok) {
|
|
60274
|
+
showToast(body.error || `Action "${op}" failed`, "error");
|
|
60275
|
+
return;
|
|
60276
|
+
}
|
|
60277
|
+
showToast(`${op}d ${photon}:${method}`, "success");
|
|
60278
|
+
await this._refresh();
|
|
60279
|
+
} catch (err) {
|
|
60280
|
+
showToast(err instanceof Error ? err.message : String(err), "error");
|
|
60281
|
+
}
|
|
60282
|
+
}
|
|
60283
|
+
_renderAddForm() {
|
|
60284
|
+
const usedNames = /* @__PURE__ */ new Set([
|
|
60285
|
+
...this._active.map((r7) => r7.method),
|
|
60286
|
+
...this._declared.map((r7) => r7.method)
|
|
60287
|
+
]);
|
|
60288
|
+
const candidates = this.availableMethods.filter((m3) => !usedNames.has(m3));
|
|
60289
|
+
if (this._formMethod && !candidates.includes(this._formMethod)) {
|
|
60290
|
+
candidates.unshift(this._formMethod);
|
|
60291
|
+
}
|
|
60292
|
+
const trimmedCron = this._formCron.trim();
|
|
60293
|
+
const cronShapeValid = isCronShapeValid(trimmedCron);
|
|
60294
|
+
const humanized = cronShapeValid ? humanizeCron2(trimmedCron) : "";
|
|
60295
|
+
const cronHasFriendlyName = humanized && humanized !== trimmedCron;
|
|
60296
|
+
const canSubmit = !!this._formMethod && cronShapeValid && !this._formSubmitting;
|
|
60297
|
+
return b2`
|
|
60298
|
+
<div class="add-form" role="group" aria-label="Add schedule">
|
|
60299
|
+
<div class="field">
|
|
60300
|
+
<span class="field-label">Method</span>
|
|
60301
|
+
<select
|
|
60302
|
+
aria-label="Method"
|
|
60303
|
+
.value=${this._formMethod}
|
|
60304
|
+
@change=${(e8) => this._formMethod = e8.target.value}
|
|
60305
|
+
>
|
|
60306
|
+
<option value="" disabled ?selected=${!this._formMethod}>Pick a method…</option>
|
|
60307
|
+
${candidates.map(
|
|
60308
|
+
(m3) => b2`<option value=${m3} ?selected=${m3 === this._formMethod}>${m3}</option>`
|
|
60309
|
+
)}
|
|
60310
|
+
</select>
|
|
60311
|
+
</div>
|
|
60312
|
+
|
|
60313
|
+
<div class="field">
|
|
60314
|
+
<span class="field-label">Preset</span>
|
|
60315
|
+
<div class="preset-row" role="group" aria-label="Cron presets">
|
|
60316
|
+
${CRON_PRESETS.map(
|
|
60317
|
+
(p5) => b2`
|
|
60318
|
+
<button
|
|
60319
|
+
type="button"
|
|
60320
|
+
class="preset-chip ${trimmedCron === p5.cron ? "active" : ""}"
|
|
60321
|
+
title="${p5.cron}"
|
|
60322
|
+
@click=${() => this._formCron = p5.cron}
|
|
60323
|
+
>
|
|
60324
|
+
${p5.label}
|
|
60325
|
+
</button>
|
|
60326
|
+
`
|
|
60327
|
+
)}
|
|
60328
|
+
</div>
|
|
60329
|
+
</div>
|
|
60330
|
+
|
|
60331
|
+
<div class="field">
|
|
60332
|
+
<span class="field-label">Cron</span>
|
|
60333
|
+
<input
|
|
60334
|
+
aria-label="Cron expression"
|
|
60335
|
+
type="text"
|
|
60336
|
+
placeholder="0 9 * * *"
|
|
60337
|
+
class=${trimmedCron && !cronShapeValid ? "invalid" : ""}
|
|
60338
|
+
spellcheck="false"
|
|
60339
|
+
autocomplete="off"
|
|
60340
|
+
.value=${this._formCron}
|
|
60341
|
+
@input=${(e8) => this._formCron = e8.target.value}
|
|
60342
|
+
@keydown=${(e8) => {
|
|
60343
|
+
if (e8.key === "Enter" && canSubmit) void this._addManual();
|
|
60344
|
+
if (e8.key === "Escape") this._showAddForm = false;
|
|
60345
|
+
}}
|
|
60346
|
+
/>
|
|
60347
|
+
</div>
|
|
60348
|
+
|
|
60349
|
+
<div
|
|
60350
|
+
class="preview-line ${trimmedCron && !cronShapeValid ? "error" : ""}"
|
|
60351
|
+
role="status"
|
|
60352
|
+
aria-live="polite"
|
|
60353
|
+
>
|
|
60354
|
+
${!trimmedCron ? b2`<span>Pick a preset or type a cron expression.</span>` : !cronShapeValid ? b2`<span>Cron must have 5 fields: minute hour day month weekday.</span>` : cronHasFriendlyName ? b2`<span class="preview-arrow">→</span>
|
|
60355
|
+
<span class="preview-text">${humanized}</span>` : b2`<span class="preview-arrow">→</span>
|
|
60356
|
+
<span class="preview-text">${trimmedCron}</span>
|
|
60357
|
+
<span>(custom schedule)</span>`}
|
|
60358
|
+
</div>
|
|
60359
|
+
|
|
60360
|
+
<div class="add-actions">
|
|
60361
|
+
<span class="add-hint">
|
|
60362
|
+
Need help?
|
|
60363
|
+
<a href="https://crontab.guru/" target="_blank" rel="noopener">crontab.guru</a>
|
|
60364
|
+
</span>
|
|
60365
|
+
<span style="display: flex; gap: 6px;">
|
|
60366
|
+
<button
|
|
60367
|
+
?disabled=${this._formSubmitting}
|
|
60368
|
+
@click=${() => {
|
|
60369
|
+
this._showAddForm = false;
|
|
60370
|
+
}}
|
|
60371
|
+
>
|
|
60372
|
+
Cancel
|
|
60373
|
+
</button>
|
|
60374
|
+
<button class="primary" ?disabled=${!canSubmit} @click=${() => this._addManual()}>
|
|
60375
|
+
${this._formSubmitting ? "Saving\u2026" : "Save"}
|
|
60376
|
+
</button>
|
|
60377
|
+
</span>
|
|
60378
|
+
</div>
|
|
60379
|
+
</div>
|
|
60380
|
+
`;
|
|
60381
|
+
}
|
|
60382
|
+
_renderSchedules() {
|
|
60383
|
+
const total = this._active.length + this._declared.length;
|
|
60384
|
+
return b2`
|
|
60385
|
+
<div class="section">
|
|
60386
|
+
<div class="section-header">
|
|
60387
|
+
<h3 class="section-title">
|
|
60388
|
+
Schedules ${total > 0 ? b2`<span class="section-count">${total}</span>` : ""}
|
|
60389
|
+
</h3>
|
|
60390
|
+
${this._showAddForm ? "" : b2`<button
|
|
60391
|
+
class="add-btn"
|
|
60392
|
+
title="Add a manual cron schedule for any method"
|
|
60393
|
+
@click=${() => {
|
|
60394
|
+
this._showAddForm = true;
|
|
60395
|
+
if (!this._formMethod) {
|
|
60396
|
+
const used = /* @__PURE__ */ new Set([
|
|
60397
|
+
...this._active.map((r7) => r7.method),
|
|
60398
|
+
...this._declared.map((r7) => r7.method)
|
|
60399
|
+
]);
|
|
60400
|
+
this._formMethod = this.availableMethods.find((m3) => !used.has(m3)) ?? this.availableMethods[0] ?? "";
|
|
60401
|
+
}
|
|
60402
|
+
}}
|
|
60403
|
+
>
|
|
60404
|
+
+ Add schedule
|
|
60405
|
+
</button>`}
|
|
60406
|
+
</div>
|
|
60407
|
+
${this._showAddForm ? this._renderAddForm() : ""}
|
|
60408
|
+
${total === 0 && !this._showAddForm ? b2`<div class="empty-hint" role="status">
|
|
60409
|
+
No scheduled work for ${this.photonName}. Add a
|
|
60410
|
+
<code>@scheduled <cron></code> JSDoc tag to a method, or use the "+ Add
|
|
60411
|
+
schedule" button above to set one up manually.
|
|
60412
|
+
</div>` : total === 0 ? "" : b2`<div class="schedule-list">
|
|
60413
|
+
${this._active.map(
|
|
60414
|
+
(r7) => b2`
|
|
60415
|
+
<div class="schedule-row" role="listitem">
|
|
60416
|
+
<span class="schedule-method" title="${r7.method}">
|
|
60417
|
+
${formatLabel(r7.method)}
|
|
60418
|
+
${r7.createdBy === "manual" ? b2`<span class="manual-badge" title="Added manually via Pulse"
|
|
60419
|
+
>manual</span
|
|
60420
|
+
>` : ""}
|
|
60421
|
+
</span>
|
|
60422
|
+
<span class="schedule-cron" title="${r7.cron}">${humanizeCron2(r7.cron)}</span>
|
|
60423
|
+
<span class="schedule-next">next: ${formatWhen2(r7.nextRun)}</span>
|
|
60424
|
+
<span class="schedule-actions">
|
|
60425
|
+
<button @click=${() => this._action("pause", r7.photon, r7.method)}>
|
|
60426
|
+
Pause
|
|
60427
|
+
</button>
|
|
60428
|
+
<button @click=${() => this._action("disable", r7.photon, r7.method)}>
|
|
60429
|
+
Disable
|
|
60430
|
+
</button>
|
|
60431
|
+
</span>
|
|
60432
|
+
</div>
|
|
60433
|
+
`
|
|
60434
|
+
)}
|
|
60435
|
+
${this._declared.map(
|
|
60436
|
+
(r7) => b2`
|
|
60437
|
+
<div class="schedule-row dormant" role="listitem">
|
|
60438
|
+
<span class="schedule-method" title="${r7.method}"
|
|
60439
|
+
>${formatLabel(r7.method)}</span
|
|
60440
|
+
>
|
|
60441
|
+
<span class="schedule-cron" title="${r7.cron}">${humanizeCron2(r7.cron)}</span>
|
|
60442
|
+
<span class="schedule-next">declared, not enrolled</span>
|
|
60443
|
+
<span class="schedule-actions">
|
|
60444
|
+
<button
|
|
60445
|
+
class="primary"
|
|
60446
|
+
@click=${() => this._action("enable", r7.photon, r7.method)}
|
|
60447
|
+
>
|
|
60448
|
+
Enable
|
|
60449
|
+
</button>
|
|
60450
|
+
</span>
|
|
60451
|
+
</div>
|
|
60452
|
+
`
|
|
60453
|
+
)}
|
|
60454
|
+
</div>`}
|
|
60455
|
+
</div>
|
|
60456
|
+
`;
|
|
60457
|
+
}
|
|
60458
|
+
render() {
|
|
60459
|
+
return b2`
|
|
60460
|
+
${this._renderSchedules()}
|
|
60461
|
+
<div class="section activity">
|
|
60462
|
+
<activity-log
|
|
60463
|
+
.items=${this.activity}
|
|
60464
|
+
.filter=${this.photonName}
|
|
60465
|
+
.fullscreen=${true}
|
|
60466
|
+
@clear=${() => this.dispatchEvent(
|
|
60467
|
+
new CustomEvent("clear-activity", { bubbles: true, composed: true })
|
|
60468
|
+
)}
|
|
60469
|
+
></activity-log>
|
|
60470
|
+
</div>
|
|
60471
|
+
`;
|
|
60472
|
+
}
|
|
60473
|
+
};
|
|
60474
|
+
PhotonPulse.styles = [
|
|
60475
|
+
theme,
|
|
60476
|
+
i`
|
|
60477
|
+
:host {
|
|
60478
|
+
display: flex;
|
|
60479
|
+
flex-direction: column;
|
|
60480
|
+
flex: 1;
|
|
60481
|
+
min-height: 0;
|
|
60482
|
+
gap: var(--space-lg);
|
|
60483
|
+
}
|
|
60484
|
+
|
|
60485
|
+
.section {
|
|
60486
|
+
display: flex;
|
|
60487
|
+
flex-direction: column;
|
|
60488
|
+
gap: var(--space-sm);
|
|
60489
|
+
min-height: 0;
|
|
60490
|
+
}
|
|
60491
|
+
|
|
60492
|
+
.section.activity {
|
|
60493
|
+
flex: 1;
|
|
60494
|
+
min-height: 0;
|
|
60495
|
+
}
|
|
60496
|
+
|
|
60497
|
+
.section-header {
|
|
60498
|
+
display: flex;
|
|
60499
|
+
align-items: center;
|
|
60500
|
+
justify-content: space-between;
|
|
60501
|
+
gap: var(--space-sm);
|
|
60502
|
+
padding: 0 var(--space-xs);
|
|
60503
|
+
}
|
|
60504
|
+
|
|
60505
|
+
.section-title {
|
|
60506
|
+
margin: 0;
|
|
60507
|
+
font-size: var(--text-sm);
|
|
60508
|
+
text-transform: uppercase;
|
|
60509
|
+
letter-spacing: 0.1em;
|
|
60510
|
+
color: var(--t-primary);
|
|
60511
|
+
font-weight: 600;
|
|
60512
|
+
}
|
|
60513
|
+
|
|
60514
|
+
.section-count {
|
|
60515
|
+
color: var(--t-muted);
|
|
60516
|
+
font-size: var(--text-xs);
|
|
60517
|
+
font-weight: 500;
|
|
60518
|
+
letter-spacing: normal;
|
|
60519
|
+
text-transform: none;
|
|
60520
|
+
}
|
|
60521
|
+
|
|
60522
|
+
.schedule-list {
|
|
60523
|
+
display: flex;
|
|
60524
|
+
flex-direction: column;
|
|
60525
|
+
gap: var(--space-xs);
|
|
60526
|
+
}
|
|
60527
|
+
|
|
60528
|
+
.schedule-row {
|
|
60529
|
+
display: grid;
|
|
60530
|
+
grid-template-columns: minmax(140px, 1fr) minmax(140px, 1fr) minmax(140px, auto) auto;
|
|
60531
|
+
align-items: center;
|
|
60532
|
+
gap: var(--space-md);
|
|
60533
|
+
padding: var(--space-sm) var(--space-md);
|
|
60534
|
+
background: var(--bg-glass);
|
|
60535
|
+
border: 1px solid var(--border-glass);
|
|
60536
|
+
border-radius: var(--radius-sm);
|
|
60537
|
+
}
|
|
60538
|
+
|
|
60539
|
+
.schedule-row.dormant {
|
|
60540
|
+
opacity: 0.7;
|
|
60541
|
+
}
|
|
60542
|
+
|
|
60543
|
+
.schedule-method {
|
|
60544
|
+
color: var(--t-primary);
|
|
60545
|
+
font-weight: 500;
|
|
60546
|
+
overflow: hidden;
|
|
60547
|
+
text-overflow: ellipsis;
|
|
60548
|
+
white-space: nowrap;
|
|
60549
|
+
display: inline-flex;
|
|
60550
|
+
align-items: center;
|
|
60551
|
+
gap: 6px;
|
|
60552
|
+
}
|
|
60553
|
+
|
|
60554
|
+
.manual-badge {
|
|
60555
|
+
font-size: 9px;
|
|
60556
|
+
text-transform: uppercase;
|
|
60557
|
+
letter-spacing: 0.08em;
|
|
60558
|
+
padding: 1px 6px;
|
|
60559
|
+
border-radius: 999px;
|
|
60560
|
+
background: color-mix(in srgb, var(--accent-primary) 18%, transparent);
|
|
60561
|
+
border: 1px solid color-mix(in srgb, var(--accent-primary) 36%, var(--border-glass));
|
|
60562
|
+
color: var(--t-primary);
|
|
60563
|
+
font-weight: 600;
|
|
60564
|
+
}
|
|
60565
|
+
|
|
60566
|
+
.schedule-cron {
|
|
60567
|
+
color: var(--t-muted);
|
|
60568
|
+
font-size: var(--text-sm);
|
|
60569
|
+
font-family: var(--font-mono);
|
|
60570
|
+
}
|
|
60571
|
+
|
|
60572
|
+
.schedule-next {
|
|
60573
|
+
color: var(--t-muted);
|
|
60574
|
+
font-size: var(--text-sm);
|
|
60575
|
+
font-variant-numeric: tabular-nums;
|
|
60576
|
+
}
|
|
60577
|
+
|
|
60578
|
+
.schedule-actions {
|
|
60579
|
+
display: flex;
|
|
60580
|
+
gap: 4px;
|
|
60581
|
+
justify-self: end;
|
|
60582
|
+
}
|
|
60583
|
+
|
|
60584
|
+
.schedule-actions button {
|
|
60585
|
+
font-size: var(--text-xs);
|
|
60586
|
+
padding: 3px 10px;
|
|
60587
|
+
border: 1px solid var(--border-glass);
|
|
60588
|
+
background: transparent;
|
|
60589
|
+
color: var(--t-primary);
|
|
60590
|
+
cursor: pointer;
|
|
60591
|
+
border-radius: var(--radius-xs);
|
|
60592
|
+
transition: all 0.15s ease;
|
|
60593
|
+
}
|
|
60594
|
+
|
|
60595
|
+
.schedule-actions button:hover {
|
|
60596
|
+
background: var(--bg-glass-strong);
|
|
60597
|
+
border-color: color-mix(in srgb, var(--accent-primary) 38%, var(--border-glass));
|
|
60598
|
+
}
|
|
60599
|
+
|
|
60600
|
+
.schedule-actions button.primary {
|
|
60601
|
+
background: color-mix(in srgb, var(--accent-primary) 18%, var(--bg-glass));
|
|
60602
|
+
border-color: color-mix(in srgb, var(--accent-primary) 40%, var(--border-glass));
|
|
60603
|
+
}
|
|
60604
|
+
|
|
60605
|
+
.empty-hint {
|
|
60606
|
+
color: var(--t-muted);
|
|
60607
|
+
font-size: var(--text-sm);
|
|
60608
|
+
padding: var(--space-md) var(--space-md);
|
|
60609
|
+
text-align: center;
|
|
60610
|
+
border: 1px dashed var(--border-glass);
|
|
60611
|
+
border-radius: var(--radius-sm);
|
|
60612
|
+
background: color-mix(in srgb, var(--bg-glass) 50%, transparent);
|
|
60613
|
+
}
|
|
60614
|
+
|
|
60615
|
+
.add-btn {
|
|
60616
|
+
font-size: var(--text-xs);
|
|
60617
|
+
padding: 3px 10px;
|
|
60618
|
+
border: 1px solid var(--border-glass);
|
|
60619
|
+
background: color-mix(in srgb, var(--accent-primary) 14%, var(--bg-glass));
|
|
60620
|
+
color: var(--t-primary);
|
|
60621
|
+
cursor: pointer;
|
|
60622
|
+
border-radius: var(--radius-xs);
|
|
60623
|
+
transition: all 0.15s ease;
|
|
60624
|
+
}
|
|
60625
|
+
|
|
60626
|
+
.add-btn:hover {
|
|
60627
|
+
background: color-mix(in srgb, var(--accent-primary) 22%, var(--bg-glass));
|
|
60628
|
+
border-color: color-mix(in srgb, var(--accent-primary) 50%, var(--border-glass));
|
|
60629
|
+
}
|
|
60630
|
+
|
|
60631
|
+
.add-form {
|
|
60632
|
+
display: flex;
|
|
60633
|
+
flex-direction: column;
|
|
60634
|
+
gap: var(--space-sm);
|
|
60635
|
+
padding: var(--space-md);
|
|
60636
|
+
background: color-mix(in srgb, var(--accent-primary) 6%, var(--bg-glass));
|
|
60637
|
+
border: 1px solid color-mix(in srgb, var(--accent-primary) 28%, var(--border-glass));
|
|
60638
|
+
border-radius: var(--radius-sm);
|
|
60639
|
+
}
|
|
60640
|
+
|
|
60641
|
+
.field {
|
|
60642
|
+
display: grid;
|
|
60643
|
+
grid-template-columns: 90px 1fr;
|
|
60644
|
+
align-items: center;
|
|
60645
|
+
gap: var(--space-sm);
|
|
60646
|
+
}
|
|
60647
|
+
|
|
60648
|
+
.field-label {
|
|
60649
|
+
font-size: var(--text-xs);
|
|
60650
|
+
color: var(--t-muted);
|
|
60651
|
+
text-transform: uppercase;
|
|
60652
|
+
letter-spacing: 0.08em;
|
|
60653
|
+
}
|
|
60654
|
+
|
|
60655
|
+
.add-form select,
|
|
60656
|
+
.add-form input {
|
|
60657
|
+
font: inherit;
|
|
60658
|
+
font-size: var(--text-sm);
|
|
60659
|
+
color: var(--t-primary);
|
|
60660
|
+
background: var(--bg-elevated, #0b1018);
|
|
60661
|
+
border: 1px solid var(--border-glass);
|
|
60662
|
+
border-radius: var(--radius-xs);
|
|
60663
|
+
padding: 5px 8px;
|
|
60664
|
+
width: 100%;
|
|
60665
|
+
box-sizing: border-box;
|
|
60666
|
+
}
|
|
60667
|
+
|
|
60668
|
+
.add-form input {
|
|
60669
|
+
font-family: var(--font-mono);
|
|
60670
|
+
}
|
|
60671
|
+
|
|
60672
|
+
.add-form select:focus,
|
|
60673
|
+
.add-form input:focus {
|
|
60674
|
+
outline: none;
|
|
60675
|
+
border-color: color-mix(in srgb, var(--accent-primary) 60%, var(--border-glass));
|
|
60676
|
+
}
|
|
60677
|
+
|
|
60678
|
+
.add-form input.invalid {
|
|
60679
|
+
border-color: color-mix(in srgb, var(--color-warning, #ef4444) 60%, var(--border-glass));
|
|
60680
|
+
}
|
|
60681
|
+
|
|
60682
|
+
.preset-row {
|
|
60683
|
+
display: flex;
|
|
60684
|
+
flex-wrap: wrap;
|
|
60685
|
+
gap: 6px;
|
|
60686
|
+
}
|
|
60687
|
+
|
|
60688
|
+
.preset-chip {
|
|
60689
|
+
font-size: var(--text-xs);
|
|
60690
|
+
padding: 3px 10px;
|
|
60691
|
+
border: 1px solid var(--border-glass);
|
|
60692
|
+
background: var(--bg-elevated, #0b1018);
|
|
60693
|
+
color: var(--t-primary);
|
|
60694
|
+
cursor: pointer;
|
|
60695
|
+
border-radius: 999px;
|
|
60696
|
+
transition: all 0.15s ease;
|
|
60697
|
+
font-family: var(--font-mono);
|
|
60698
|
+
}
|
|
60699
|
+
|
|
60700
|
+
.preset-chip:hover {
|
|
60701
|
+
background: color-mix(in srgb, var(--accent-primary) 14%, var(--bg-elevated));
|
|
60702
|
+
border-color: color-mix(in srgb, var(--accent-primary) 38%, var(--border-glass));
|
|
60703
|
+
}
|
|
60704
|
+
|
|
60705
|
+
.preset-chip.active {
|
|
60706
|
+
background: color-mix(in srgb, var(--accent-primary) 22%, var(--bg-elevated));
|
|
60707
|
+
border-color: color-mix(in srgb, var(--accent-primary) 55%, var(--border-glass));
|
|
60708
|
+
color: var(--accent-primary);
|
|
60709
|
+
}
|
|
60710
|
+
|
|
60711
|
+
.preview-line {
|
|
60712
|
+
font-size: var(--text-xs);
|
|
60713
|
+
color: var(--t-muted);
|
|
60714
|
+
display: flex;
|
|
60715
|
+
align-items: center;
|
|
60716
|
+
gap: 6px;
|
|
60717
|
+
padding-left: calc(90px + var(--space-sm));
|
|
60718
|
+
min-height: 18px;
|
|
60719
|
+
}
|
|
60720
|
+
|
|
60721
|
+
.preview-line.error {
|
|
60722
|
+
color: var(--color-warning, #ef4444);
|
|
60723
|
+
}
|
|
60724
|
+
|
|
60725
|
+
.preview-arrow {
|
|
60726
|
+
opacity: 0.5;
|
|
60727
|
+
}
|
|
60728
|
+
|
|
60729
|
+
.preview-text {
|
|
60730
|
+
color: var(--t-primary);
|
|
60731
|
+
font-weight: 500;
|
|
60732
|
+
}
|
|
60733
|
+
|
|
60734
|
+
.add-actions {
|
|
60735
|
+
display: flex;
|
|
60736
|
+
justify-content: space-between;
|
|
60737
|
+
align-items: center;
|
|
60738
|
+
gap: var(--space-sm);
|
|
60739
|
+
}
|
|
60740
|
+
|
|
60741
|
+
.add-form button {
|
|
60742
|
+
font-size: var(--text-xs);
|
|
60743
|
+
padding: 5px 14px;
|
|
60744
|
+
border: 1px solid var(--border-glass);
|
|
60745
|
+
background: transparent;
|
|
60746
|
+
color: var(--t-primary);
|
|
60747
|
+
cursor: pointer;
|
|
60748
|
+
border-radius: var(--radius-xs);
|
|
60749
|
+
transition: all 0.15s ease;
|
|
60750
|
+
}
|
|
60751
|
+
|
|
60752
|
+
.add-form button.primary {
|
|
60753
|
+
background: color-mix(in srgb, var(--accent-primary) 18%, var(--bg-glass));
|
|
60754
|
+
border-color: color-mix(in srgb, var(--accent-primary) 40%, var(--border-glass));
|
|
60755
|
+
}
|
|
60756
|
+
|
|
60757
|
+
.add-form button:hover:not([disabled]) {
|
|
60758
|
+
background: var(--bg-glass-strong);
|
|
60759
|
+
}
|
|
60760
|
+
|
|
60761
|
+
.add-form button[disabled] {
|
|
60762
|
+
opacity: 0.5;
|
|
60763
|
+
cursor: not-allowed;
|
|
60764
|
+
}
|
|
60765
|
+
|
|
60766
|
+
.add-hint {
|
|
60767
|
+
font-size: var(--text-xs);
|
|
60768
|
+
color: var(--t-muted);
|
|
60769
|
+
}
|
|
60770
|
+
|
|
60771
|
+
.add-hint a {
|
|
60772
|
+
color: var(--accent-primary);
|
|
60773
|
+
text-decoration: none;
|
|
60774
|
+
}
|
|
60775
|
+
|
|
60776
|
+
.add-hint a:hover {
|
|
60777
|
+
text-decoration: underline;
|
|
60778
|
+
}
|
|
60779
|
+
|
|
60780
|
+
@media (max-width: 640px) {
|
|
60781
|
+
.schedule-row {
|
|
60782
|
+
grid-template-columns: 1fr;
|
|
60783
|
+
gap: var(--space-xs);
|
|
60784
|
+
}
|
|
60785
|
+
|
|
60786
|
+
.schedule-actions {
|
|
60787
|
+
justify-self: start;
|
|
60788
|
+
}
|
|
60789
|
+
}
|
|
60790
|
+
`
|
|
60791
|
+
];
|
|
60792
|
+
__decorateClass([
|
|
60793
|
+
n4({ type: String })
|
|
60794
|
+
], PhotonPulse.prototype, "photonName", 2);
|
|
60795
|
+
__decorateClass([
|
|
60796
|
+
n4({ type: Array })
|
|
60797
|
+
], PhotonPulse.prototype, "availableMethods", 2);
|
|
60798
|
+
__decorateClass([
|
|
60799
|
+
n4({ type: Array })
|
|
60800
|
+
], PhotonPulse.prototype, "activity", 2);
|
|
60801
|
+
__decorateClass([
|
|
60802
|
+
r5()
|
|
60803
|
+
], PhotonPulse.prototype, "_active", 2);
|
|
60804
|
+
__decorateClass([
|
|
60805
|
+
r5()
|
|
60806
|
+
], PhotonPulse.prototype, "_declared", 2);
|
|
60807
|
+
__decorateClass([
|
|
60808
|
+
r5()
|
|
60809
|
+
], PhotonPulse.prototype, "_error", 2);
|
|
60810
|
+
__decorateClass([
|
|
60811
|
+
r5()
|
|
60812
|
+
], PhotonPulse.prototype, "_showAddForm", 2);
|
|
60813
|
+
__decorateClass([
|
|
60814
|
+
r5()
|
|
60815
|
+
], PhotonPulse.prototype, "_formMethod", 2);
|
|
60816
|
+
__decorateClass([
|
|
60817
|
+
r5()
|
|
60818
|
+
], PhotonPulse.prototype, "_formCron", 2);
|
|
60819
|
+
__decorateClass([
|
|
60820
|
+
r5()
|
|
60821
|
+
], PhotonPulse.prototype, "_formSubmitting", 2);
|
|
60822
|
+
PhotonPulse = __decorateClass([
|
|
60823
|
+
t4("photon-pulse")
|
|
60824
|
+
], PhotonPulse);
|
|
60825
|
+
|
|
59417
60826
|
// src/auto-ui/frontend/components/custom-ui-renderer.ts
|
|
59418
60827
|
init_lit();
|
|
59419
60828
|
init_decorators();
|