@memberjunction/ng-dashboards 6.2.0-edge.4 → 6.2.0-edge.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.
Files changed (87) hide show
  1. package/dist/AI/components/vectors/sensitive-fields.d.ts +53 -0
  2. package/dist/AI/components/vectors/sensitive-fields.d.ts.map +1 -0
  3. package/dist/AI/components/vectors/sensitive-fields.js +226 -0
  4. package/dist/AI/components/vectors/sensitive-fields.js.map +1 -0
  5. package/dist/AI/components/vectors/vector-document-rules.d.ts +126 -0
  6. package/dist/AI/components/vectors/vector-document-rules.d.ts.map +1 -0
  7. package/dist/AI/components/vectors/vector-document-rules.js +168 -0
  8. package/dist/AI/components/vectors/vector-document-rules.js.map +1 -0
  9. package/dist/AI/components/vectors/vector-management-resource.component.d.ts +49 -0
  10. package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
  11. package/dist/AI/components/vectors/vector-management-resource.component.js +428 -230
  12. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  13. package/dist/Communication/communication-agent-context.d.ts +11 -1
  14. package/dist/Communication/communication-agent-context.d.ts.map +1 -1
  15. package/dist/Communication/communication-agent-context.js +15 -0
  16. package/dist/Communication/communication-agent-context.js.map +1 -1
  17. package/dist/Communication/communication-dashboard.component.d.ts.map +1 -1
  18. package/dist/Communication/communication-dashboard.component.js +5 -0
  19. package/dist/Communication/communication-dashboard.component.js.map +1 -1
  20. package/dist/Communication/communication-providers-resource.component.d.ts +27 -1
  21. package/dist/Communication/communication-providers-resource.component.d.ts.map +1 -1
  22. package/dist/Communication/communication-providers-resource.component.js +722 -78
  23. package/dist/Communication/communication-providers-resource.component.js.map +1 -1
  24. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +25 -0
  25. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  26. package/dist/DataExplorer/data-explorer-dashboard.component.js +74 -5
  27. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  28. package/dist/DevTools/app-state-inspector.component.d.ts.map +1 -1
  29. package/dist/DevTools/app-state-inspector.component.js +1 -0
  30. package/dist/DevTools/app-state-inspector.component.js.map +1 -1
  31. package/dist/DevTools/class-registry.component.d.ts.map +1 -1
  32. package/dist/DevTools/class-registry.component.js +1 -0
  33. package/dist/DevTools/class-registry.component.js.map +1 -1
  34. package/dist/DevTools/event-monitor.component.d.ts.map +1 -1
  35. package/dist/DevTools/event-monitor.component.js +2 -0
  36. package/dist/DevTools/event-monitor.component.js.map +1 -1
  37. package/dist/DevTools/graphql-console.component.d.ts.map +1 -1
  38. package/dist/DevTools/graphql-console.component.js.map +1 -1
  39. package/dist/DevTools/grid-width-lab.component.d.ts.map +1 -1
  40. package/dist/DevTools/grid-width-lab.component.js +1 -0
  41. package/dist/DevTools/grid-width-lab.component.js.map +1 -1
  42. package/dist/DevTools/layout-inspector.component.d.ts.map +1 -1
  43. package/dist/DevTools/layout-inspector.component.js +1 -0
  44. package/dist/DevTools/layout-inspector.component.js.map +1 -1
  45. package/dist/DevTools/lazy-module-status.component.d.ts.map +1 -1
  46. package/dist/DevTools/lazy-module-status.component.js +1 -0
  47. package/dist/DevTools/lazy-module-status.component.js.map +1 -1
  48. package/dist/DevTools/settings-explorer.component.d.ts.map +1 -1
  49. package/dist/DevTools/settings-explorer.component.js +1 -0
  50. package/dist/DevTools/settings-explorer.component.js.map +1 -1
  51. package/dist/DevTools/tab-strip-lab.component.d.ts.map +1 -1
  52. package/dist/DevTools/tab-strip-lab.component.js +1 -0
  53. package/dist/DevTools/tab-strip-lab.component.js.map +1 -1
  54. package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts +5 -0
  55. package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts.map +1 -1
  56. package/dist/EntityAdmin/entity-admin-dashboard.component.js +8 -5
  57. package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
  58. package/dist/Integration/components/overview/overview.component.js +2 -2
  59. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +2 -2
  60. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
  61. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts +11 -1
  62. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts.map +1 -1
  63. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js +24 -2
  64. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js.map +1 -1
  65. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts +16 -2
  66. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts.map +1 -1
  67. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +87 -33
  68. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
  69. package/dist/LoginStudio/login-studio-inspector.component.d.ts +130 -0
  70. package/dist/LoginStudio/login-studio-inspector.component.d.ts.map +1 -0
  71. package/dist/LoginStudio/login-studio-inspector.component.js +781 -0
  72. package/dist/LoginStudio/login-studio-inspector.component.js.map +1 -0
  73. package/dist/LoginStudio/login-studio-resource.component.d.ts +153 -0
  74. package/dist/LoginStudio/login-studio-resource.component.d.ts.map +1 -0
  75. package/dist/LoginStudio/login-studio-resource.component.js +857 -0
  76. package/dist/LoginStudio/login-studio-resource.component.js.map +1 -0
  77. package/dist/Workflows/components/workflow-runs-resource.component.js +4 -4
  78. package/dist/Workflows/components/workflow-runs-resource.component.js.map +1 -1
  79. package/dist/core-dashboards.module.d.ts +2 -1
  80. package/dist/core-dashboards.module.d.ts.map +1 -1
  81. package/dist/core-dashboards.module.js +11 -3
  82. package/dist/core-dashboards.module.js.map +1 -1
  83. package/dist/public-api.d.ts +1 -0
  84. package/dist/public-api.d.ts.map +1 -1
  85. package/dist/public-api.js +1 -0
  86. package/dist/public-api.js.map +1 -1
  87. package/package.json +72 -69
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-studio-resource.component.js","sourceRoot":"","sources":["../../src/LoginStudio/login-studio-resource.component.ts","../../src/LoginStudio/login-studio-resource.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Login Studio: design the Explorer sign-in page as data, on the default theme.\n * @module LoginStudio\n */\n\nimport { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnInit, inject } from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\nimport { CdkDrag, CdkDropList, CdkDropListGroup, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';\nimport { takeUntil } from 'rxjs';\nimport { LogError, RunView } from '@memberjunction/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { MJThemeEntity, ResourceData } from '@memberjunction/core-entities';\nimport { BaseResourceComponent, ThemeService } from '@memberjunction/ng-shared';\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\nimport {\n MJAlertComponent, MJButtonDirective, MJConfirmService, MJEmptyStateComponent, MJPageBodyComponent, MJPageHeaderComponent,\n MJPageLayoutComponent, MJStatBadgeComponent, MJViewToggleComponent, type MJStatBadgeVariant, type ViewToggleOption,\n} from '@memberjunction/ng-ui-components';\nimport { MJLoginRegionComponent, type MJLoginRegionCommand, type MJLoginRegionDrop } from '@memberjunction/ng-auth-services';\nimport {\n CloneLoginBlock, DefaultLoginDesign, IsLoginHexColor, IsSafeLoginImageURL, LOGIN_BLOCK_META, LOGIN_BLOCK_TYPES, LOGIN_LIMITS,\n LOGIN_REGION_LABEL, LOGIN_REGIONS, LocateLoginBlock, LoginDesignProblems, LoginDesignTokens, LoginThemeTokens, NewLoginBlock,\n ParseLoginDesign, PublicLoginScreenFromTheme,\n type LoginBackgroundKind, type LoginBlock, type LoginBlockType, type LoginDesign, type LoginRegion, type ThemeLogos, type ThemeSeeds,\n} from '@memberjunction/theme-engine';\nimport { LoginStudioInspectorComponent, type LoginBackgroundField, type LoginEdit, type LoginSurface } from './login-studio-inspector.component';\n\n/** The drop list the palette's tiles come from. Not a region, so a drop from it CREATES a block. */\nconst PALETTE = 'palette';\n/** A run of edits to one field within this window is one undo step, not one per keystroke. */\nconst COALESCE_MS = 800;\nconst HISTORY_CAP = 60;\n/** The click a browser fires as a drag ends; ignored so a drop keeps its selection. */\nconst DROP_CLICK_MS = 400;\nconst SURFACE_LABEL: Record<LoginSurface, string> = { Banner: 'Story panel', Panel: 'Sign-in column' };\nconst SURFACE_ICON: Record<LoginSurface, string> = { Banner: 'fa-solid fa-image', Panel: 'fa-solid fa-right-to-bracket' };\n\n/**\n * LOGIN STUDIO: an administrator designs the page visitors see before they sign in. The design lives on\n * the default Active theme (`LoginScreenDraft` while edited, `LoginScreen` once published), because a\n * sign-in page belongs to a brand. The preview is drawn by `<mj-login-region>`, the renderer the live\n * login screen uses; every drag also has a click or key (Add, Alt+Arrow, Delete).\n *\n * SAFETY BOUNDARY (agent): the agent reads the design and its problems (GetSignInPageDesign). Save draft,\n * Publish and \"Use the stock sign-in page\" are human-only: the published page is what every visitor meets\n * before signing in.\n */\n@RegisterClass(BaseResourceComponent, 'LoginStudioResource')\n@Component({\n standalone: true,\n selector: 'mj-login-studio-resource',\n imports: [\n CdkDropListGroup, CdkDropList, CdkDrag, SharedGenericModule, MJAlertComponent, MJButtonDirective, MJEmptyStateComponent,\n MJPageLayoutComponent, MJPageHeaderComponent, MJPageBodyComponent, MJStatBadgeComponent, MJViewToggleComponent,\n MJLoginRegionComponent, LoginStudioInspectorComponent,\n ],\n templateUrl: './login-studio-resource.component.html',\n styleUrls: ['./login-studio-resource.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LoginStudioResourceComponent extends BaseResourceComponent implements OnInit, AfterViewInit {\n private readonly cdr = inject(ChangeDetectorRef);\n private readonly confirm = inject(MJConfirmService);\n private readonly themeService = inject(ThemeService);\n private readonly doc = inject(DOCUMENT);\n\n public readonly Palette = LOGIN_BLOCK_TYPES.map((type) => ({ Type: type, ...LOGIN_BLOCK_META[type] }));\n public readonly PaletteListID = PALETTE;\n public readonly RegionLabel = LOGIN_REGION_LABEL;\n public readonly DeviceOptions: ViewToggleOption[] = [\n { key: 'desktop', label: 'Desktop', icon: 'fa-solid fa-desktop' },\n { key: 'mobile', label: 'Phone', icon: 'fa-solid fa-mobile-screen' },\n ];\n\n public Loading = true;\n public Saving = false;\n /** A failure to show, or null. */\n public ErrorMessage: string | null = null;\n /** A confirmation to show, or null. */\n public NoticeMessage: string | null = null;\n\n /** The theme being designed for: the default Active theme, or null when there is none. */\n public Theme: MJThemeEntity | null = null;\n public Design: LoginDesign = DefaultLoginDesign();\n public SelectedID: string | null = null;\n /** The story panel or the sign-in column, when one is selected instead of a block. */\n public SelectedSurface: LoginSurface | null = null;\n public Device: 'desktop' | 'mobile' = 'desktop';\n public Logos: ThemeLogos = {};\n /** The default theme's look, as custom properties on the preview frame. */\n public FrameStyle: Record<string, string> = {};\n\n /** The problems that stopped the last save; re-checked as you edit, so the alert goes once they are fixed. */\n private problems: string[] = [];\n /** What is stored, as JSON, so \"unsaved\" and \"unpublished\" are comparisons rather than flags. */\n private savedDraft: string | null = null;\n private published: string | null = null;\n /** The design as it stands in storage (or the starter, before anything is saved). */\n private baseline = JSON.stringify(this.Design);\n private seeds: ThemeSeeds | null = null;\n private undo: string[] = [];\n private redo: string[] = [];\n private lastEdit: { key: string; at: number } | null = null;\n private droppedAt = 0;\n private reportedEditing = false;\n\n public override ngOnInit(): void {\n super.ngOnInit();\n // The preview restates the stock gradient per color mode, so it follows a light/dark switch.\n this.themeService.AppliedTheme$.pipe(takeUntil(this.destroy$)).subscribe(() => this.changed());\n void this.Reload();\n }\n\n public ngAfterViewInit(): void {\n this.registerAgentTools();\n }\n\n public async GetResourceDisplayName(_data: ResourceData): Promise<string> {\n return 'Sign-in Page';\n }\n\n public async GetResourceIconClass(_data: ResourceData): Promise<string> {\n return 'fa-solid fa-right-to-bracket';\n }\n\n /** Unsaved edits: the shell keeps this tab rather than replace it. */\n public override IsEditing(): boolean {\n return this.Unsaved;\n }\n\n // ── Loading ─────────────────────────────────────────────────────────────────────────────────\n\n /** Re-read the default theme and its sign-in page. */\n public async Reload(): Promise<void> {\n this.Loading = true;\n this.ErrorMessage = null;\n this.cdr.markForCheck();\n try {\n // The theme GET /auth/login-screen serves: the default Active one, most recently changed first.\n const rv = await RunView.FromMetadataProvider(this.ProviderToUse).RunView<MJThemeEntity>({\n EntityName: 'MJ: Themes',\n ExtraFilter: `IsDefault = 1 AND Status = 'Active'`,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 1,\n ResultType: 'entity_object',\n });\n if (rv.Success) {\n this.adoptTheme(rv.Results[0] ?? null);\n } else {\n this.ErrorMessage = rv.ErrorMessage || 'Could not read the default theme.';\n }\n } catch (e) {\n LogError(e);\n this.ErrorMessage = e instanceof Error ? e.message : String(e);\n } finally {\n this.Loading = false;\n this.changed();\n this.NotifyLoadComplete();\n }\n }\n\n /** Take the stored state from the theme, and edit its draft (else its live page, else the starter). */\n private adoptTheme(theme: MJThemeEntity | null): void {\n this.readStored(theme);\n this.Design = ParseLoginDesign(this.savedDraft ?? this.published) ?? DefaultLoginDesign();\n this.baseline = this.CurrentJson;\n this.undo = [];\n this.redo = [];\n this.SelectedID = null;\n this.SelectedSurface = null;\n this.problems = [];\n }\n\n /** What is stored on the theme: what is saved, what is live, and its seeds and logos. */\n private readStored(theme: MJThemeEntity | null): void {\n this.Theme = theme;\n // Seeds and logos as the live page gets them, so the preview wears exactly what visitors will see.\n const pub = theme ? PublicLoginScreenFromTheme(theme) : null;\n this.Logos = pub?.Logos ?? {};\n this.seeds = pub?.Seeds ?? null;\n const published = ParseLoginDesign(theme?.LoginScreen);\n const draft = ParseLoginDesign(theme?.LoginScreenDraft);\n this.published = published ? JSON.stringify(published) : null;\n this.savedDraft = draft ? JSON.stringify(draft) : null;\n }\n\n // ── State ───────────────────────────────────────────────────────────────────────────────────\n\n public get CurrentJson(): string {\n return JSON.stringify(this.Design);\n }\n /** Edits not yet saved as the draft. */\n public get Unsaved(): boolean {\n return this.CurrentJson !== this.baseline;\n }\n /** The saved draft differs from what visitors see. */\n public get Unpublished(): boolean {\n return this.savedDraft !== null && this.savedDraft !== this.published;\n }\n public get IsLive(): boolean {\n return this.published !== null;\n }\n public get CanUndo(): boolean {\n return this.undo.length > 0;\n }\n public get CanRedo(): boolean {\n return this.redo.length > 0;\n }\n public get Stacked(): boolean {\n return this.Device === 'mobile';\n }\n\n public get StatusLabel(): string {\n if (this.Unsaved) return 'Unsaved changes';\n if (this.Unpublished) return 'Draft not published';\n if (this.IsLive) return 'Published';\n return 'Stock sign-in page';\n }\n /** The header pill: shown only when there is something to say (live, or work waiting on you). */\n public get StatusVariant(): MJStatBadgeVariant | null {\n if (this.Unsaved || this.Unpublished) return 'warning';\n return this.IsLive ? 'success' : null;\n }\n\n public get Selected(): LoginBlock | null {\n const hit = this.SelectedID ? LocateLoginBlock(this.Design, this.SelectedID) : null;\n return hit ? this.Design.Regions[hit.Region][hit.Index] : null;\n }\n /** The region of the selected block: a card's colors depend on which side of the page it is on. */\n public get SelectedRegion(): LoginRegion | null {\n return this.SelectedID ? LocateLoginBlock(this.Design, this.SelectedID)?.Region ?? null : null;\n }\n /** The selected surface; a selected block always wins. */\n public get Surface(): LoginSurface | null {\n return this.SelectedID ? null : this.SelectedSurface;\n }\n public get SideTitle(): string {\n if (this.Selected) return LOGIN_BLOCK_META[this.Selected.Type].Label;\n return this.Surface ? SURFACE_LABEL[this.Surface] : 'Page';\n }\n public get SideIcon(): string {\n if (this.Selected) return 'fa-solid fa-sliders';\n return this.Surface ? SURFACE_ICON[this.Surface] : 'fa-solid fa-table-columns';\n }\n\n /** Closing or reloading the browser with unsaved edits asks first. */\n @HostListener('window:beforeunload', ['$event'])\n public OnBeforeUnload(e: BeforeUnloadEvent): void {\n if (this.Unsaved) e.preventDefault();\n }\n\n // ── Saving ──────────────────────────────────────────────────────────────────────────────────\n\n public async SaveDraft(): Promise<void> {\n if (!this.checkBeforeWrite()) return;\n const json = this.CurrentJson;\n if (await this.write((t) => { t.LoginScreenDraft = json; })) {\n this.baseline = json;\n this.done('Draft saved. Visitors still see the published page until you publish.');\n }\n }\n\n public async Publish(): Promise<void> {\n if (!this.checkBeforeWrite()) return;\n const json = this.CurrentJson;\n if (await this.write((t) => { t.LoginScreenDraft = json; t.LoginScreen = json; })) {\n this.baseline = json;\n // GET /auth/login-screen lets browsers keep its answer for a minute.\n this.done('Published. Visitors see the new sign-in page within a minute.');\n }\n }\n\n /** Back to what is stored (the saved draft, the live page, or the starter), dropping unsaved edits. */\n public Discard(): void {\n this.pushHistory();\n this.lastEdit = null;\n this.Design = JSON.parse(this.baseline) as LoginDesign;\n this.SelectedID = null;\n this.problems = [];\n this.ErrorMessage = null;\n this.NoticeMessage = null;\n this.changed();\n }\n\n /** Remove the custom page: visitors get the stock sign-in page again. */\n public async UseStockPage(): Promise<void> {\n const ok = await this.confirm.ConfirmDelete({\n title: 'Use the stock sign-in page?',\n message: 'Visitors will see the stock sign-in page, and this design and its draft are removed.',\n confirmText: 'Remove the custom page',\n cancelText: 'Keep it',\n });\n if (!ok) return;\n if (await this.write((t) => { t.LoginScreenDraft = null; t.LoginScreen = null; })) {\n this.pushHistory();\n this.lastEdit = null;\n this.Design = DefaultLoginDesign();\n this.baseline = this.CurrentJson;\n this.SelectedID = null;\n this.done('Visitors now see the stock sign-in page.');\n }\n }\n\n public DismissError(): void {\n this.ErrorMessage = null;\n this.cdr.markForCheck();\n }\n\n public DismissNotice(): void {\n this.NoticeMessage = null;\n this.cdr.markForCheck();\n }\n\n private done(notice: string): void {\n this.NoticeMessage = notice;\n this.changed();\n }\n\n /** The editor's own list of problems, in its words. The entity's Validate runs the full check on save. */\n private checkBeforeWrite(): boolean {\n this.NoticeMessage = null;\n this.problems = LoginDesignProblems(this.Design);\n this.ErrorMessage = this.problems.length ? this.problemsMessage() : null;\n this.cdr.markForCheck();\n return this.problems.length === 0;\n }\n\n private problemsMessage(): string {\n return `Fix these before saving: ${this.problems.join(' ')}`;\n }\n\n /**\n * Writes the sign-in page columns to a FRESH copy of the theme, so a save never carries back a stale\n * copy of the theme's other columns (another admin may have changed its colors or the default meanwhile).\n */\n private async write(apply: (t: MJThemeEntity) => void): Promise<boolean> {\n if (!this.Theme) return false;\n this.Saving = true;\n this.cdr.markForCheck();\n try {\n const theme = await this.ProviderToUse.GetEntityObject<MJThemeEntity>('MJ: Themes');\n if (!(await theme.Load(this.Theme.ID))) {\n this.ErrorMessage = 'The theme could not be read, so nothing was saved. Try again.';\n return false;\n }\n if (!theme.IsDefault || theme.Status !== 'Active') {\n this.ErrorMessage = `${theme.Name} is no longer the default theme, so nothing was saved. Close and reopen Login Studio to design the current one.`;\n return false;\n }\n apply(theme);\n if (!(await theme.Save())) {\n this.ErrorMessage = theme.LatestResult?.CompleteMessage || 'The save was refused.';\n return false;\n }\n // What is stored now, which another admin may have changed since this page loaded.\n this.readStored(theme);\n return true;\n } catch (e) {\n LogError(e);\n this.ErrorMessage = e instanceof Error ? e.message : String(e);\n return false;\n } finally {\n this.Saving = false;\n this.cdr.markForCheck();\n }\n }\n\n // ── Editing ─────────────────────────────────────────────────────────────────────────────────\n\n /** Every change goes through here: one undo step (or one per run of typing into a field) and a fresh object for OnPush. */\n private mutate(fn: (d: LoginDesign) => void, coalesceKey?: string): void {\n const now = Date.now();\n const coalesce = !!coalesceKey && this.lastEdit?.key === coalesceKey && now - this.lastEdit.at < COALESCE_MS;\n if (!coalesce) this.pushHistory();\n this.lastEdit = coalesceKey ? { key: coalesceKey, at: now } : null;\n const next = JSON.parse(this.CurrentJson) as LoginDesign;\n fn(next);\n this.Design = next;\n this.changed();\n }\n\n /** What the inspector asks for, applied as one undoable step. */\n public ApplyEdit(e: LoginEdit): void {\n switch (e.Kind) {\n case 'Block':\n return this.editSelected((b) => { Object.assign(b, { [e.Field]: e.Value }); }, e.Field);\n case 'Link':\n return this.editSelected((b) => { if (b.Type === 'links' && b.Links[e.Index]) b.Links[e.Index][e.Field] = e.Value; }, `link${e.Index}:${e.Field}`);\n case 'AddLink':\n return this.editSelected((b) => { if (b.Type === 'links' && b.Links.length < LOGIN_LIMITS.Links) b.Links.push({ Label: '', Href: 'https://' }); });\n case 'RemoveLink':\n return this.editSelected((b) => { if (b.Type === 'links') b.Links.splice(e.Index, 1); });\n case 'Page':\n return this.mutate((d) => { Object.assign(d, { [e.Field]: e.Value }); });\n case 'Heading':\n return this.mutate((d) => { d.Heading = e.Value.trim() ? e.Value.slice(0, LOGIN_LIMITS.Heading) : null; }, 'heading');\n case 'Background':\n return this.editBackground(e.Field, e.Value);\n case 'Color':\n return this.mutate((d) => {\n const v = typeof e.Value === 'string' ? e.Value.trim() : '';\n if (v) d.Colors[e.Key] = v;\n else delete d.Colors[e.Key];\n }, `color:${e.Key}`);\n }\n }\n\n private editSelected(fn: (b: LoginBlock) => void, coalesceField?: string): void {\n const id = this.SelectedID;\n if (!id) return;\n this.mutate((d) => {\n const hit = LocateLoginBlock(d, id);\n if (hit) fn(d.Regions[hit.Region][hit.Index]);\n }, coalesceField ? `${id}:${coalesceField}` : undefined);\n }\n\n private editBackground(field: LoginBackgroundField, value: string | number | null): void {\n this.mutate((d) => {\n const bg = d.Background;\n if (field === 'Kind') {\n bg.Kind = value as LoginBackgroundKind;\n // A half-typed value would block saving from a field this kind no longer shows.\n if (bg.Color && !IsLoginHexColor(bg.Color)) bg.Color = null;\n if (bg.Color2 && !IsLoginHexColor(bg.Color2)) bg.Color2 = null;\n if (bg.Kind !== 'image' && bg.ImageURL && !IsSafeLoginImageURL(bg.ImageURL)) bg.ImageURL = null;\n // Start a color choice from the theme's own brand colors rather than an empty picker.\n const primary = IsLoginHexColor(this.seeds?.primary) ? this.seeds.primary : null;\n const accent = IsLoginHexColor(this.seeds?.accent) ? this.seeds.accent : primary;\n if ((bg.Kind === 'color' || bg.Kind === 'gradient') && !bg.Color) bg.Color = primary;\n if (bg.Kind === 'gradient' && !bg.Color2) bg.Color2 = accent;\n } else if (field === 'Overlay') {\n bg.Overlay = Math.max(0, Math.min(LOGIN_LIMITS.OverlayMax, Math.round(Number(value) || 0)));\n } else {\n bg[field] = typeof value === 'string' && value.trim() ? value.trim() : null;\n }\n }, `bg:${field}`);\n }\n\n private pushHistory(): void {\n this.undo.push(this.CurrentJson);\n if (this.undo.length > HISTORY_CAP) this.undo.shift();\n this.redo = [];\n }\n\n public Undo(): void {\n const prev = this.undo.pop();\n if (prev === undefined) return;\n this.redo.push(this.CurrentJson);\n this.restore(prev);\n }\n\n public Redo(): void {\n const next = this.redo.pop();\n if (next === undefined) return;\n this.undo.push(this.CurrentJson);\n this.restore(next);\n }\n\n /** A snapshot this editor took itself, so it comes back exactly as it was, half-typed values included. */\n private restore(json: string): void {\n this.Design = JSON.parse(json) as LoginDesign;\n this.lastEdit = null;\n if (this.SelectedID && !LocateLoginBlock(this.Design, this.SelectedID)) this.SelectedID = null;\n this.changed();\n }\n\n /** Ctrl/Cmd+Z and Shift+Ctrl/Cmd+Z while focus is in the studio, except in a field being typed in. */\n @HostListener('keydown', ['$event'])\n public OnKeydown(e: KeyboardEvent): void {\n const target = e.target as HTMLElement | null;\n if (target && (/^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName) || target.isContentEditable)) return;\n if (!(e.metaKey || e.ctrlKey) || e.key.toLowerCase() !== 'z') return;\n e.preventDefault();\n if (e.shiftKey) this.Redo();\n else this.Undo();\n }\n\n // ── Blocks ──────────────────────────────────────────────────────────────────────────────────\n\n /** A click on the empty preview deselects, except the click that ends a drag. */\n public OnStageClick(): void {\n if (Date.now() - this.droppedAt < DROP_CLICK_MS) return;\n this.Select(null);\n }\n\n public OnDrop(e: MJLoginRegionDrop): void {\n this.droppedAt = Date.now();\n const to = e.To;\n if (typeof e.Item === 'string') {\n this.AddBlock(e.Item, to, e.CurrentIndex);\n return;\n }\n const from = LOGIN_REGIONS.find((r) => r === e.From);\n if (!from || (from === to && e.PreviousIndex === e.CurrentIndex)) return;\n if (from !== to && this.regionFull(to)) return;\n this.mutate((d) => {\n if (from === to) moveItemInArray(d.Regions[to], e.PreviousIndex, e.CurrentIndex);\n else transferArrayItem(d.Regions[from], d.Regions[to], e.PreviousIndex, e.CurrentIndex);\n });\n }\n\n /** The palette never accepts a drop: dragging a tile back onto it cancels. */\n public readonly NoEnter = (): boolean => false;\n\n /**\n * Add a block: dropped at a spot, or (clicked in the palette) just after the selected block, else at\n * the end of the selected surface's first region, else of the story panel.\n */\n public AddBlock(type: LoginBlockType, region?: LoginRegion, index?: number): void {\n const sel = !region && this.SelectedID ? LocateLoginBlock(this.Design, this.SelectedID) : null;\n const where: LoginRegion = region ?? sel?.Region ?? (this.Surface === 'Panel' ? 'PanelTop' : 'Banner');\n if (this.regionFull(where)) return;\n const at = index ?? (sel ? sel.Index + 1 : this.Design.Regions[where].length);\n const block = NewLoginBlock(type);\n this.SelectedID = block.ID;\n this.SelectedSurface = null;\n this.mutate((d) => d.Regions[where].splice(at, 0, block));\n }\n\n public OnCommand(c: MJLoginRegionCommand): void {\n const hit = LocateLoginBlock(this.Design, c.ID);\n if (!hit) return;\n const { Region: region, Index: index } = hit;\n switch (c.Kind) {\n case 'up':\n case 'down': {\n const to = index + (c.Kind === 'up' ? -1 : 1);\n if (to < 0 || to >= this.Design.Regions[region].length) return;\n this.mutate((d) => moveItemInArray(d.Regions[region], index, to));\n break;\n }\n case 'duplicate': {\n if (this.regionFull(region)) return;\n const copy = CloneLoginBlock(this.Design.Regions[region][index]);\n this.SelectedID = copy.ID;\n this.mutate((d) => d.Regions[region].splice(index + 1, 0, copy));\n break;\n }\n case 'delete':\n this.SelectedID = null;\n this.mutate((d) => d.Regions[region].splice(index, 1));\n break;\n }\n }\n\n /** True, with a notice, when a region has no room for another block. */\n private regionFull(region: LoginRegion): boolean {\n if (this.Design.Regions[region].length < LOGIN_LIMITS.BlocksPerRegion) return false;\n this.NoticeMessage = `${LOGIN_REGION_LABEL[region]} already has ${LOGIN_LIMITS.BlocksPerRegion} blocks.`;\n this.cdr.markForCheck();\n return true;\n }\n\n public Select(id: string | null): void {\n this.SelectedID = id;\n this.SelectedSurface = null;\n this.publishAgentContext();\n this.cdr.markForCheck();\n }\n\n /** Select the story panel or the sign-in column (a click on it in the preview, or the inspector's buttons). */\n public SelectSurface(surface: LoginSurface, e?: Event): void {\n if (e) {\n e.stopPropagation();\n if (Date.now() - this.droppedAt < DROP_CLICK_MS) return;\n }\n this.SelectedID = null;\n this.SelectedSurface = surface;\n this.publishAgentContext();\n this.cdr.markForCheck();\n }\n\n public SetDevice(device: string): void {\n this.Device = device === 'mobile' ? 'mobile' : 'desktop';\n this.cdr.markForCheck();\n }\n\n // ── Preview, agent, shell ───────────────────────────────────────────────────────────────────\n\n /** After any change: the problems alert, the preview, the agent's view, the shell's edit state, and the view. */\n private changed(): void {\n // A save refused for problems re-checks as you edit (or undo), and its alert goes once they are fixed.\n if (this.problems.length) {\n this.problems = LoginDesignProblems(this.Design);\n this.ErrorMessage = this.problems.length ? this.problemsMessage() : null;\n }\n // The frame wears the DEFAULT theme's look, which is not necessarily the viewer's, as the live login screen does.\n const dark = this.doc.documentElement.getAttribute('data-theme') === 'dark';\n this.FrameStyle = { ...LoginThemeTokens(this.seeds, dark), ...LoginDesignTokens(this.Design) };\n this.publishAgentContext();\n if (this.Unsaved !== this.reportedEditing) {\n this.reportedEditing = this.Unsaved;\n this.NotifyEditModeChanged(this.reportedEditing);\n }\n this.cdr.markForCheck();\n }\n\n private publishAgentContext(): void {\n this.navigationService.SetAgentContext(this, {\n Surface: 'Login Studio',\n ThemeName: this.Theme?.Name ?? null,\n Status: this.StatusLabel,\n Layout: this.Design.Layout,\n BlockCounts: Object.fromEntries(LOGIN_REGIONS.map((r) => [r, this.Design.Regions[r].length])),\n Selected: this.Selected ? LOGIN_BLOCK_META[this.Selected.Type].Label : this.Surface,\n Problems: LoginDesignProblems(this.Design),\n });\n }\n\n private registerAgentTools(): void {\n this.navigationService.SetAgentClientTools(this, [\n {\n Name: 'GetSignInPageDesign',\n Description: 'Return the sign-in page design being edited (JSON), its status, and anything that would stop it being saved.',\n ParameterSchema: { type: 'object', properties: {} },\n Handler: async () => ({\n Success: true,\n Data: { Status: this.StatusLabel, Design: this.Design, Problems: LoginDesignProblems(this.Design) },\n }),\n },\n ]);\n }\n}\n","<mj-page-layout>\n <mj-page-header Title=\"Sign-in Page\" Icon=\"fa-solid fa-right-to-bracket\"\n [Subtitle]=\"Theme ? 'What visitors see before they sign in. Designed on the default theme, ' + Theme.Name + '.' : 'What visitors see before they sign in.'\">\n <!-- One root element per block: a control-flow block is projected by its single root's selector. -->\n @if (Theme && !Loading && StatusVariant) {\n <mj-stat-badge meta [Label]=\"StatusLabel\" [Variant]=\"StatusVariant\"></mj-stat-badge>\n }\n @if (Theme && !Loading) {\n <div actions class=\"ls__actions\">\n <button type=\"button\" mjButton Variant=\"icon\" Size=\"sm\" (click)=\"Undo()\" [disabled]=\"!CanUndo\" title=\"Undo (Ctrl/Cmd Z)\" AriaLabel=\"Undo\"><i class=\"fa-solid fa-rotate-left\"></i></button>\n <button type=\"button\" mjButton Variant=\"icon\" Size=\"sm\" (click)=\"Redo()\" [disabled]=\"!CanRedo\" title=\"Redo (Shift Ctrl/Cmd Z)\" AriaLabel=\"Redo\"><i class=\"fa-solid fa-rotate-right\"></i></button>\n <button type=\"button\" mjButton Variant=\"outline\" Size=\"sm\" (click)=\"Discard()\" [disabled]=\"!Unsaved || Saving\">Discard changes</button>\n <button type=\"button\" mjButton Variant=\"outline\" Size=\"sm\" (click)=\"SaveDraft()\" [disabled]=\"Saving || !Unsaved\">\n <i class=\"fa-regular fa-floppy-disk\"></i> Save draft\n </button>\n <button type=\"button\" mjButton Variant=\"primary\" Size=\"sm\" (click)=\"Publish()\"\n [disabled]=\"Saving || (!Unsaved && !Unpublished && IsLive)\" title=\"Save and show this page to everyone signing in\">\n <i class=\"fa-solid fa-upload\"></i> Publish\n </button>\n </div>\n }\n </mj-page-header>\n\n <mj-page-body>\n @if (ErrorMessage) {\n <mj-alert class=\"ls__alert\" Variant=\"error\" [Message]=\"ErrorMessage\" Dismissible (Dismissed)=\"DismissError()\"></mj-alert>\n }\n @if (NoticeMessage) {\n <mj-alert class=\"ls__alert\" Variant=\"success\" [Message]=\"NoticeMessage\" Dismissible (Dismissed)=\"DismissNotice()\"></mj-alert>\n }\n\n @if (Loading) {\n <mj-loading Text=\"Reading the sign-in page...\" Size=\"medium\"></mj-loading>\n } @else if (!Theme) {\n <mj-empty-state Icon=\"fa-solid fa-palette\" Title=\"No default theme\"\n Message=\"The sign-in page is designed on the default theme. Set one in Theme Studio, under Manage Themes.\"></mj-empty-state>\n } @else {\n <div class=\"ls\" cdkDropListGroup>\n <!-- PALETTE -->\n <aside class=\"ls__palette\" aria-label=\"Blocks\">\n <h2 class=\"ls__heading\">Blocks</h2>\n <p class=\"ls__hint\">Drag a block onto the page, or click one to add it after the selected block.</p>\n <div class=\"ls__palette-list\" cdkDropList [cdkDropListData]=\"PaletteListID\" cdkDropListSortingDisabled\n [cdkDropListEnterPredicate]=\"NoEnter\">\n @for (t of Palette; track t.Type) {\n <button type=\"button\" mjButton Variant=\"outline\" Size=\"sm\" class=\"ls__tile\" cdkDrag [cdkDragData]=\"t.Type\" (click)=\"AddBlock(t.Type)\" [title]=\"t.Hint\">\n <i [class]=\"t.Icon\" aria-hidden=\"true\"></i>\n <span>{{ t.Label }}</span>\n </button>\n }\n </div>\n </aside>\n\n <!-- PREVIEW -->\n <section class=\"ls__stage\" (click)=\"OnStageClick()\" aria-label=\"Preview\">\n <div class=\"ls__stage-bar\" (click)=\"$event.stopPropagation()\">\n <mj-view-toggle role=\"group\" aria-label=\"Preview size\" [Options]=\"DeviceOptions\" [ActiveKey]=\"Device\" (KeyChange)=\"SetDevice($event)\"></mj-view-toggle>\n <span class=\"ls__hint\">The sign-in button is a stand-in. The real one opens your sign-in provider.</span>\n </div>\n\n <div class=\"ls__frame\" [class.ls__frame--phone]=\"Stacked\" [style]=\"FrameStyle\">\n <!-- Clicking the page selects what was clicked: a block (it stops the click), the sign-in column,\n or else the story panel, which in the centered layout is the whole background. -->\n <div class=\"lp\" [class.lp--reverse]=\"Design.Layout === 'split-reverse'\" [class.lp--centered]=\"Design.Layout === 'centered'\"\n [class.lp--stacked]=\"Stacked\" [class.lp--surface-on]=\"Surface === 'Banner' && Design.Layout === 'centered'\"\n (click)=\"SelectSurface('Banner', $event)\">\n <div class=\"lp__banner\" [class.lp__banner--center]=\"Design.Alignment === 'center' || Stacked\"\n [class.lp__surface-on]=\"Surface === 'Banner' && Design.Layout !== 'centered'\">\n <div class=\"lp__brand\">\n <span class=\"lp__region-label\">{{ RegionLabel.Banner }}</span>\n <mj-login-region Region=\"Banner\" [Blocks]=\"Design.Regions.Banner\" [Logos]=\"Logos\" [BrandName]=\"Theme.Name\"\n [Stacked]=\"Stacked\" [Center]=\"Design.Alignment === 'center' || Stacked\" [Editable]=\"true\"\n [SelectedID]=\"SelectedID\"\n (Dropped)=\"OnDrop($event)\" (SelectRequested)=\"Select($event)\" (CommandRequested)=\"OnCommand($event)\"></mj-login-region>\n @if (!Design.Regions.Banner.length) {\n <p class=\"lp__default-note\">Empty: visitors see the stock logo and \"Welcome back\" here.</p>\n }\n </div>\n </div>\n <div class=\"lp__panel\" [class.lp__surface-on]=\"Surface === 'Panel'\" (click)=\"SelectSurface('Panel', $event)\">\n <div class=\"lp__stack\">\n <span class=\"lp__region-label\">{{ RegionLabel.PanelTop }}</span>\n <mj-login-region Region=\"PanelTop\" [Blocks]=\"Design.Regions.PanelTop\" [Logos]=\"Logos\" [BrandName]=\"Theme.Name\"\n [Stacked]=\"Stacked\" [Editable]=\"true\" [SelectedID]=\"SelectedID\"\n (Dropped)=\"OnDrop($event)\" (SelectRequested)=\"Select($event)\" (CommandRequested)=\"OnCommand($event)\"></mj-login-region>\n <div class=\"lp__picker\" aria-hidden=\"true\">\n @if (Design.Heading) {\n <span class=\"lp__heading\">{{ Design.Heading }}</span>\n }\n <span class=\"lp__signin\">Log in</span>\n </div>\n <span class=\"lp__region-label\">{{ RegionLabel.PanelBottom }}</span>\n <mj-login-region Region=\"PanelBottom\" [Blocks]=\"Design.Regions.PanelBottom\" [Logos]=\"Logos\" [BrandName]=\"Theme.Name\"\n [Stacked]=\"Stacked\" [Editable]=\"true\" [SelectedID]=\"SelectedID\"\n (Dropped)=\"OnDrop($event)\" (SelectRequested)=\"Select($event)\" (CommandRequested)=\"OnCommand($event)\"></mj-login-region>\n </div>\n </div>\n </div>\n </div>\n </section>\n\n <!-- INSPECTOR -->\n <aside class=\"ls__side\" aria-label=\"Settings\">\n <div class=\"ls__side-head\">\n <i [class]=\"SideIcon\" aria-hidden=\"true\"></i>\n <h2 class=\"ls__side-title\">{{ SideTitle }}</h2>\n @if (Selected || Surface) {\n <button type=\"button\" mjButton Variant=\"icon\" Size=\"sm\" (click)=\"Select(null)\" title=\"Page settings\" AriaLabel=\"Back to page settings\">\n <i class=\"fa-solid fa-table-columns\"></i>\n </button>\n }\n </div>\n <mj-login-studio-inspector [Design]=\"Design\" [Selected]=\"Selected\" [SelectedRegion]=\"SelectedRegion\" [Surface]=\"Surface\"\n [CanReset]=\"IsLive || Unpublished\" [Saving]=\"Saving\"\n (EditRequested)=\"ApplyEdit($event)\" (CommandRequested)=\"OnCommand($event)\"\n (StockPageRequested)=\"UseStockPage()\" (SurfaceSelectRequested)=\"SelectSurface($event)\"></mj-login-studio-inspector>\n </aside>\n </div>\n }\n </mj-page-body>\n</mj-page-layout>\n"]}
@@ -748,7 +748,7 @@ function WorkflowRunsResourceComponent_Conditional_16_Conditional_22_Conditional
748
748
  i0.ɵɵadvance();
749
749
  i0.ɵɵproperty("size", ctx_r0.Layout.StepPanelOpen ? ctx_r0.Layout.StepSplitSizes[0] : 100)("minSize", 25);
750
750
  i0.ɵɵadvance(2);
751
- i0.ɵɵproperty("Provider", ctx_r0.ProviderToUse)("ParentTaskID", selected_r9.ID)("WorkflowName", selected_r9.Name)("LiveUpdates", selected_r9.Status === "Running" || selected_r9.Status === "In Progress")("ReplayAt", ctx_r0.ReplayAt)("ShowLegend", ctx_r0.Layout.ShowLegend);
751
+ i0.ɵɵproperty("Provider", ctx_r0.ProviderToUse)("ParentTaskID", selected_r9.ID)("WorkflowName", selected_r9.Name)("LiveUpdates", selected_r9.Status === "Running" || selected_r9.Status === "In Progress")("ReplayAt", ctx_r0.ReplayAt)("ShowLegend", ctx_r0.Layout.ShowLegend)("ShowVariables", false);
752
752
  i0.ɵɵadvance(2);
753
753
  i0.ɵɵconditional(ctx_r0.CanReplay ? 5 : -1);
754
754
  i0.ɵɵadvance();
@@ -795,7 +795,7 @@ function WorkflowRunsResourceComponent_Conditional_16_Conditional_22_Template(rf
795
795
  i0.ɵɵtext(18, " JSON ");
796
796
  i0.ɵɵelementEnd()();
797
797
  i0.ɵɵconditionalCreate(19, WorkflowRunsResourceComponent_Conditional_16_Conditional_22_Conditional_19_Template, 9, 5, "div", 46);
798
- i0.ɵɵconditionalCreate(20, WorkflowRunsResourceComponent_Conditional_16_Conditional_22_Conditional_20_Template, 12, 17, "as-split", 47)(21, WorkflowRunsResourceComponent_Conditional_16_Conditional_22_Conditional_21_Template, 2, 3, "div", 48);
798
+ i0.ɵɵconditionalCreate(20, WorkflowRunsResourceComponent_Conditional_16_Conditional_22_Conditional_20_Template, 12, 18, "as-split", 47)(21, WorkflowRunsResourceComponent_Conditional_16_Conditional_22_Conditional_21_Template, 2, 3, "div", 48);
799
799
  i0.ɵɵelementEnd();
800
800
  } if (rf & 2) {
801
801
  const selected_r9 = ctx;
@@ -1796,7 +1796,7 @@ let WorkflowRunsResourceComponent = class WorkflowRunsResourceComponent extends
1796
1796
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.GraphDebug = _t.first);
1797
1797
  } }, hostBindings: function WorkflowRunsResourceComponent_HostBindings(rf, ctx) { if (rf & 1) {
1798
1798
  i0.ɵɵlistener("resize", function WorkflowRunsResourceComponent_resize_HostBindingHandler() { return ctx.onViewportResized(); }, i0.ɵɵresolveWindow);
1799
- } }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 17, vars: 4, consts: [["GraphDebug", ""], ["Title", "Runs", "Icon", "fa-solid fa-timeline", "Subtitle", "Every workflow that has run \u2014 however it was started."], ["meta", ""], ["Label", "runs", 3, "Count", "Total"], ["actions", ""], [3, "Clicked", "Loading"], ["toolbar", ""], ["Placeholder", "Search runs", 3, "ValueChange", "Value"], [1, "wfr-status-filters"], ["type", "button", "mjButton", "", "size", "sm", 3, "variant"], ["text", "Loading runs..."], ["Variant", "error", 3, "Message"], ["Icon", "fa-solid fa-timeline", "Title", "Nothing has run yet", "Message", "When a workflow runs \u2014 started by hand, on a schedule, or by an agent \u2014 it will appear here."], ["Icon", "fa-solid fa-filter", "Title", "No runs match", "Message", "Nothing matches the current filter. Clear it to see everything that has run."], ["direction", "horizontal", "unit", "percent", 1, "wfr-split", 3, "gutterSize", "disabled"], ["type", "button", "mjButton", "", "size", "sm", 3, "click", "variant"], ["direction", "horizontal", "unit", "percent", 1, "wfr-split", 3, "dragEnd", "gutterSize", "disabled"], [3, "size", "minSize"], [1, "wfr-list"], ["role", "row", 1, "wfr-head"], ["type", "button", 1, "wfr-head-cell", "wfr-col-name", 3, "click"], ["aria-hidden", "true", 3, "ngClass"], ["type", "button", 1, "wfr-head-cell", "wfr-col-time", 3, "click"], ["type", "button", 1, "wfr-head-cell", "wfr-col-duration", 3, "click"], ["type", "button", 1, "wfr-head-cell", "wfr-col-status", 3, "click"], ["type", "button", 1, "wfr-row", 3, "wfr-row-selected"], [3, "size", "minSize", "visible"], [1, "wfr-detail"], ["type", "button", 1, "wfr-row", 3, "click"], [1, "wfr-col-name", "wfr-row-name"], [1, "wfr-col-time"], [1, "wfr-col-duration"], [1, "wfr-col-status", "wfr-row-status"], [1, "wfr-detail-head"], [1, "wfr-detail-title"], [1, "wfr-detail-name"], [1, "wfr-row-status"], [1, "wfr-detail-actions"], ["mjButton", "", "variant", "flat", "size", "sm"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "title"], ["mjButton", "", "variant", "flat", "size", "sm", "aria-label", "Close", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-xmark"], ["role", "tablist", 1, "wfr-detail-tabs"], ["type", "button", 1, "wfr-tab", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-diagram-project"], ["aria-hidden", "true", 1, "fa-solid", "fa-code"], ["role", "status", 1, "wfr-stall"], ["unit", "percent", 1, "wfr-graph-split", 3, "direction", "gutterSize"], [1, "wfr-json-pane"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-up-right-from-square"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click", "title"], ["aria-hidden", "true", 1, "fa-solid"], [1, "wfr-stall-body"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], [1, "wfr-stall-copy"], [1, "wfr-stall-reason"], [1, "wfr-stall-actions"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "disabled"], ["unit", "percent", 1, "wfr-graph-split", 3, "dragEnd", "direction", "gutterSize"], [1, "wfr-graph-pane"], ["Height", "100%", 3, "NodeSelected", "ConnectionSelected", "LegendToggled", "Settled", "Frame", "PausedChange", "Provider", "ParentTaskID", "WorkflowName", "LiveUpdates", "ReplayAt", "ShowLegend"], [1, "wfr-replay"], ["aria-label", "Armed breakpoints", 1, "wfr-bp-bar"], ["aria-label", "Engine activity", 1, "wfr-engine"], [1, "wfr-steps-pane"], ["text", "Loading steps..."], ["aria-hidden", "true", 1, "fa-solid", "fa-clock-rotate-left"], ["type", "range", "min", "0", "max", "100", "step", "1", "aria-label", "Replay position", 3, "input", "value"], [1, "wfr-chip", "wfr-chip-paused"], ["type", "button", 1, "wfr-chip", "wfr-chip-break"], ["aria-hidden", "true", 1, "fa-solid", "fa-pause"], ["type", "button", 1, "wfr-chip", "wfr-chip-break", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-circle"], ["role", "button", "tabindex", "0", "aria-label", "Remove breakpoint", 1, "wfr-chip-x", 3, "click"], [1, "wfr-tick", 3, "wfr-tick-held"], [1, "wfr-tick", "wfr-tick-paused"], [1, "wfr-tick"], [1, "wfr-step-list"], ["type", "button", 1, "wfr-step", 3, "wfr-step-selected"], [1, "wfr-step-json"], ["Size", "compact", "Icon", "fa-solid fa-hand-pointer", "Title", "Select a step", "Message", "Click a step on the canvas or in the list to see everything it recorded."], ["type", "button", 1, "wfr-step", 3, "click"], [1, "wfr-step-name"], [1, "wfr-step-meta"], [1, "wfr-step-json-head"], [1, "wfr-chip", "wfr-chip-forced"], [1, "wfr-insp-card"], [1, "wfr-insp-label"], [1, "wfr-insp-actions"], [1, "wfr-insp-hint"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "disabled"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click", "disabled"], ["role", "status", 1, "wfr-insp-card", "wfr-insp-failure"], ["type", "button", 1, "wfr-bp", 3, "click", "disabled", "title"], ["aria-hidden", "true", 1, "fa-circle"], [1, "wfr-insp-card", 3, "wfr-insp-bad"], [1, "wfr-editor-card"], [1, "wfr-editor-card", "wfr-editor-danger"], [3, "InputPayload", "OutputPayload", "Invocation"], ["mjButton", "", "variant", "secondary", "size", "sm", "title", "Declare this step not-taken; later steps proceed", 3, "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "disabled"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", "title", "Declare this step not-taken; later steps proceed", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-forward"], ["aria-hidden", "true", 1, "fa-solid", "fa-pen-to-square"], ["aria-hidden", "true", 1, "fa-solid", "fa-rotate-right"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-flag-checkered"], ["rows", "8", "spellcheck", "false", 1, "mj-textarea", "wfr-json-edit", 3, "ngModelChange", "ngModel"], [1, "wfr-editor-error"], ["rows", "6", "spellcheck", "false", 1, "mj-textarea", "wfr-json-edit", 3, "ngModelChange", "ngModel"], ["type", "text", 1, "mj-input", 3, "ngModelChange", "ngModel", "placeholder"], [2, "width", "100%", 3, "ngModel", "language", "readonly"]], template: function WorkflowRunsResourceComponent_Template(rf, ctx) { if (rf & 1) {
1799
+ } }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 17, vars: 4, consts: [["GraphDebug", ""], ["Title", "Runs", "Icon", "fa-solid fa-timeline", "Subtitle", "Every workflow that has run \u2014 however it was started."], ["meta", ""], ["Label", "runs", 3, "Count", "Total"], ["actions", ""], [3, "Clicked", "Loading"], ["toolbar", ""], ["Placeholder", "Search runs", 3, "ValueChange", "Value"], [1, "wfr-status-filters"], ["type", "button", "mjButton", "", "size", "sm", 3, "variant"], ["text", "Loading runs..."], ["Variant", "error", 3, "Message"], ["Icon", "fa-solid fa-timeline", "Title", "Nothing has run yet", "Message", "When a workflow runs \u2014 started by hand, on a schedule, or by an agent \u2014 it will appear here."], ["Icon", "fa-solid fa-filter", "Title", "No runs match", "Message", "Nothing matches the current filter. Clear it to see everything that has run."], ["direction", "horizontal", "unit", "percent", 1, "wfr-split", 3, "gutterSize", "disabled"], ["type", "button", "mjButton", "", "size", "sm", 3, "click", "variant"], ["direction", "horizontal", "unit", "percent", 1, "wfr-split", 3, "dragEnd", "gutterSize", "disabled"], [3, "size", "minSize"], [1, "wfr-list"], ["role", "row", 1, "wfr-head"], ["type", "button", 1, "wfr-head-cell", "wfr-col-name", 3, "click"], ["aria-hidden", "true", 3, "ngClass"], ["type", "button", 1, "wfr-head-cell", "wfr-col-time", 3, "click"], ["type", "button", 1, "wfr-head-cell", "wfr-col-duration", 3, "click"], ["type", "button", 1, "wfr-head-cell", "wfr-col-status", 3, "click"], ["type", "button", 1, "wfr-row", 3, "wfr-row-selected"], [3, "size", "minSize", "visible"], [1, "wfr-detail"], ["type", "button", 1, "wfr-row", 3, "click"], [1, "wfr-col-name", "wfr-row-name"], [1, "wfr-col-time"], [1, "wfr-col-duration"], [1, "wfr-col-status", "wfr-row-status"], [1, "wfr-detail-head"], [1, "wfr-detail-title"], [1, "wfr-detail-name"], [1, "wfr-row-status"], [1, "wfr-detail-actions"], ["mjButton", "", "variant", "flat", "size", "sm"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "title"], ["mjButton", "", "variant", "flat", "size", "sm", "aria-label", "Close", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-xmark"], ["role", "tablist", 1, "wfr-detail-tabs"], ["type", "button", 1, "wfr-tab", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-diagram-project"], ["aria-hidden", "true", 1, "fa-solid", "fa-code"], ["role", "status", 1, "wfr-stall"], ["unit", "percent", 1, "wfr-graph-split", 3, "direction", "gutterSize"], [1, "wfr-json-pane"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-up-right-from-square"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click", "title"], ["aria-hidden", "true", 1, "fa-solid"], [1, "wfr-stall-body"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], [1, "wfr-stall-copy"], [1, "wfr-stall-reason"], [1, "wfr-stall-actions"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "disabled"], ["unit", "percent", 1, "wfr-graph-split", 3, "dragEnd", "direction", "gutterSize"], [1, "wfr-graph-pane"], ["Height", "100%", 3, "NodeSelected", "ConnectionSelected", "LegendToggled", "Settled", "Frame", "PausedChange", "Provider", "ParentTaskID", "WorkflowName", "LiveUpdates", "ReplayAt", "ShowLegend", "ShowVariables"], [1, "wfr-replay"], ["aria-label", "Armed breakpoints", 1, "wfr-bp-bar"], ["aria-label", "Engine activity", 1, "wfr-engine"], [1, "wfr-steps-pane"], ["text", "Loading steps..."], ["aria-hidden", "true", 1, "fa-solid", "fa-clock-rotate-left"], ["type", "range", "min", "0", "max", "100", "step", "1", "aria-label", "Replay position", 3, "input", "value"], [1, "wfr-chip", "wfr-chip-paused"], ["type", "button", 1, "wfr-chip", "wfr-chip-break"], ["aria-hidden", "true", 1, "fa-solid", "fa-pause"], ["type", "button", 1, "wfr-chip", "wfr-chip-break", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-circle"], ["role", "button", "tabindex", "0", "aria-label", "Remove breakpoint", 1, "wfr-chip-x", 3, "click"], [1, "wfr-tick", 3, "wfr-tick-held"], [1, "wfr-tick", "wfr-tick-paused"], [1, "wfr-tick"], [1, "wfr-step-list"], ["type", "button", 1, "wfr-step", 3, "wfr-step-selected"], [1, "wfr-step-json"], ["Size", "compact", "Icon", "fa-solid fa-hand-pointer", "Title", "Select a step", "Message", "Click a step on the canvas or in the list to see everything it recorded."], ["type", "button", 1, "wfr-step", 3, "click"], [1, "wfr-step-name"], [1, "wfr-step-meta"], [1, "wfr-step-json-head"], [1, "wfr-chip", "wfr-chip-forced"], [1, "wfr-insp-card"], [1, "wfr-insp-label"], [1, "wfr-insp-actions"], [1, "wfr-insp-hint"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "disabled"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click", "disabled"], ["role", "status", 1, "wfr-insp-card", "wfr-insp-failure"], ["type", "button", 1, "wfr-bp", 3, "click", "disabled", "title"], ["aria-hidden", "true", 1, "fa-circle"], [1, "wfr-insp-card", 3, "wfr-insp-bad"], [1, "wfr-editor-card"], [1, "wfr-editor-card", "wfr-editor-danger"], [3, "InputPayload", "OutputPayload", "Invocation"], ["mjButton", "", "variant", "secondary", "size", "sm", "title", "Declare this step not-taken; later steps proceed", 3, "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "disabled"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", "title", "Declare this step not-taken; later steps proceed", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-forward"], ["aria-hidden", "true", 1, "fa-solid", "fa-pen-to-square"], ["aria-hidden", "true", 1, "fa-solid", "fa-rotate-right"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-flag-checkered"], ["rows", "8", "spellcheck", "false", 1, "mj-textarea", "wfr-json-edit", 3, "ngModelChange", "ngModel"], [1, "wfr-editor-error"], ["rows", "6", "spellcheck", "false", 1, "mj-textarea", "wfr-json-edit", 3, "ngModelChange", "ngModel"], ["type", "text", 1, "mj-input", 3, "ngModelChange", "ngModel", "placeholder"], [2, "width", "100%", 3, "ngModel", "language", "readonly"]], template: function WorkflowRunsResourceComponent_Template(rf, ctx) { if (rf & 1) {
1800
1800
  i0.ɵɵelementStart(0, "mj-page-layout")(1, "mj-page-header", 1)(2, "div", 2);
1801
1801
  i0.ɵɵconditionalCreate(3, WorkflowRunsResourceComponent_Conditional_3_Template, 1, 2, "mj-stat-badge", 3);
1802
1802
  i0.ɵɵelementEnd();
@@ -1831,7 +1831,7 @@ WorkflowRunsResourceComponent = WorkflowRunsResourceComponent_1 = __decorate([
1831
1831
  export { WorkflowRunsResourceComponent };
1832
1832
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(WorkflowRunsResourceComponent, [{
1833
1833
  type: Component,
1834
- args: [{ standalone: false, selector: 'mj-workflow-runs', changeDetection: ChangeDetectionStrategy.OnPush, template: "<mj-page-layout>\n <mj-page-header\n Title=\"Runs\"\n Icon=\"fa-solid fa-timeline\"\n Subtitle=\"Every workflow that has run \u2014 however it was started.\">\n\n <div meta>\n @if (!IsLoading && Runs.length > 0) {\n <mj-stat-badge\n [Count]=\"VisibleRuns.length\"\n [Total]=\"Runs.length\"\n Label=\"runs\"></mj-stat-badge>\n }\n </div>\n\n <div actions>\n <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"loadData()\"></mj-refresh-button>\n </div>\n\n <div toolbar>\n <mj-page-search\n [Value]=\"SearchText\"\n Placeholder=\"Search runs\"\n (ValueChange)=\"OnSearch($event)\"></mj-page-search>\n\n <div class=\"wfr-status-filters\">\n @for (status of StatusFilters; track status) {\n <button\n type=\"button\"\n mjButton\n [variant]=\"StatusFilter === status ? 'primary' : 'flat'\"\n size=\"sm\"\n (click)=\"OnFilterByStatus(status)\">\n {{ status === 'all' ? 'All' : status }}\n </button>\n }\n </div>\n </div>\n </mj-page-header>\n\n <mj-page-body>\n @if (IsLoading) {\n <mj-loading text=\"Loading runs...\"></mj-loading>\n } @else if (LoadError) {\n <mj-alert Variant=\"error\" [Message]=\"LoadError\"></mj-alert>\n } @else if (Runs.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-timeline\"\n Title=\"Nothing has run yet\"\n Message=\"When a workflow runs \u2014 started by hand, on a schedule, or by an agent \u2014 it will appear here.\">\n </mj-empty-state>\n } @else if (VisibleRuns.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-filter\"\n Title=\"No runs match\"\n Message=\"Nothing matches the current filter. Clear it to see everything that has run.\">\n </mj-empty-state>\n } @else {\n <as-split class=\"wfr-split\" direction=\"horizontal\" unit=\"percent\"\n [gutterSize]=\"8\" [disabled]=\"!SelectedRun\" (dragEnd)=\"OnSplitDragEnd($event.sizes)\">\n <as-split-area [size]=\"SelectedRun ? Layout.SplitSizes[0] : 100\" [minSize]=\"20\">\n <div class=\"wfr-list\">\n <!-- A header row that sorts. Every column a person scans by is sortable, because \"the\n longest run\" and \"the one that failed this morning\" are the two questions this list\n exists to answer and neither is answerable by scrolling. -->\n <div class=\"wfr-head\" role=\"row\">\n <button type=\"button\" class=\"wfr-head-cell wfr-col-name\" (click)=\"OnSort('Name')\">\n Workflow <i [ngClass]=\"SortIcon('Name')\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"wfr-head-cell wfr-col-time\" (click)=\"OnSort('StartedAt')\">\n Started <i [ngClass]=\"SortIcon('StartedAt')\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"wfr-head-cell wfr-col-time\" (click)=\"OnSort('CompletedAt')\">\n Ended <i [ngClass]=\"SortIcon('CompletedAt')\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"wfr-head-cell wfr-col-duration\" (click)=\"OnSort('Duration')\">\n Duration <i [ngClass]=\"SortIcon('Duration')\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"wfr-head-cell wfr-col-status\" (click)=\"OnSort('Status')\">\n Status <i [ngClass]=\"SortIcon('Status')\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @for (run of VisibleRuns; track run.ID) {\n <button\n type=\"button\"\n class=\"wfr-row\"\n [class.wfr-row-selected]=\"run.ID === SelectedRunID\"\n (click)=\"OnSelectRun(run)\">\n <span class=\"wfr-col-name wfr-row-name\">{{ run.Name }}</span>\n <!-- An em dash, never a fabricated time: a run that has not started has no start, and\n printing one would read as data. -->\n <span class=\"wfr-col-time\">{{ run.StartedAt ? (run.StartedAt | date:'MMM d, h:mm:ss a') : '\u2014' }}</span>\n <span class=\"wfr-col-time\">{{ run.CompletedAt ? (run.CompletedAt | date:'MMM d, h:mm:ss a') : '\u2014' }}</span>\n <span class=\"wfr-col-duration\">{{ run.Duration }}</span>\n <span class=\"wfr-col-status wfr-row-status\" [attr.data-status]=\"run.Status\">{{ run.Status }}</span>\n </button>\n }\n </div>\n </as-split-area>\n\n <as-split-area [size]=\"SelectedRun ? Layout.SplitSizes[1] : 0\" [minSize]=\"25\" [visible]=\"!!SelectedRun\">\n @if (SelectedRun; as selected) {\n <!-- The canvas lives in the RIGHT PANEL on selection, not nested inside the list row.\n A graph needs room to be read; inlining one per row turns a scannable list into a\n stack of diagrams. -->\n <aside class=\"wfr-detail\">\n <header class=\"wfr-detail-head\">\n <div class=\"wfr-detail-title\">\n <span class=\"wfr-detail-name\">{{ selected.Name }}</span>\n <span class=\"wfr-row-status\" [attr.data-status]=\"selected.Status\">{{ selected.Status }}</span>\n </div>\n <div class=\"wfr-detail-actions\">\n @if (selected.AgentRunID) {\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"OnOpenAgentRun(selected)\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\" aria-hidden=\"true\"></i>\n Agent run\n </button>\n }\n @if (DetailTab === 'graph') {\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"ToggleStepPanel()\"\n [title]=\"Layout.StepPanelOpen ? 'Hide step details' : 'Show step details'\">\n <i class=\"fa-solid\" [class.fa-square-caret-right]=\"Layout.StepPanelOpen\"\n [class.fa-square-caret-left]=\"!Layout.StepPanelOpen\" aria-hidden=\"true\"></i>\n </button>\n }\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"OnSelectRun(selected)\" aria-label=\"Close\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </header>\n\n <div class=\"wfr-detail-tabs\" role=\"tablist\">\n <button type=\"button\" class=\"wfr-tab\" [class.wfr-tab-active]=\"DetailTab === 'graph'\"\n (click)=\"OnDetailTab('graph')\">\n <i class=\"fa-solid fa-diagram-project\" aria-hidden=\"true\"></i> Workflow\n </button>\n <button type=\"button\" class=\"wfr-tab\" [class.wfr-tab-active]=\"DetailTab === 'json'\"\n (click)=\"OnDetailTab('json')\">\n <i class=\"fa-solid fa-code\" aria-hidden=\"true\"></i> JSON\n </button>\n </div>\n\n @if (Stall) {\n <!-- The hardening review's signature failure \u2014 \"the run strands silently\" \u2014 made\n loud. A held path is now something the operator can answer, not just read. -->\n <div class=\"wfr-stall\" role=\"status\" [attr.data-kind]=\"Stall.kind\">\n <div class=\"wfr-stall-body\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n <div class=\"wfr-stall-copy\">\n <span>{{ Stall.message }}</span>\n @if (Stall.conditionText) {\n <code>{{ Stall.conditionText }}</code>\n }\n @if (Stall.reason) {\n <span class=\"wfr-stall-reason\">{{ Stall.reason }}</span>\n }\n </div>\n </div>\n @if (Stall.kind === 'held' && Stall.edgeID) {\n <div class=\"wfr-stall-actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(Stall.edgeID!, 'true')\">\n Answer true\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(Stall.edgeID!, 'false')\">\n Answer false\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"OnDismissStall()\">\n Leave held\n </button>\n </div>\n }\n </div>\n }\n\n @if (DetailTab === 'graph') {\n <!-- Canvas BESIDE the step it opened, not above it. A JSON record in a strip along the\n bottom shows about ten lines, which is not enough of a record to read. -->\n <as-split class=\"wfr-graph-split\" [direction]=\"InnerSplitDirection\" unit=\"percent\"\n [gutterSize]=\"8\" (dragEnd)=\"OnStepSplitDragEnd($event.sizes)\">\n <as-split-area [size]=\"Layout.StepPanelOpen ? Layout.StepSplitSizes[0] : 100\" [minSize]=\"25\">\n <div class=\"wfr-graph-pane\">\n <mj-task-graph-debugger\n #GraphDebug\n [Provider]=\"ProviderToUse\"\n [ParentTaskID]=\"selected.ID\"\n [WorkflowName]=\"selected.Name\"\n [LiveUpdates]=\"selected.Status === 'Running' || selected.Status === 'In Progress'\"\n [ReplayAt]=\"ReplayAt\"\n [ShowLegend]=\"Layout.ShowLegend\"\n Height=\"100%\"\n (NodeSelected)=\"OnGraphNodeSelected($event)\"\n (ConnectionSelected)=\"OnGraphConnectionSelected($event)\"\n (LegendToggled)=\"OnLegendToggled($event)\"\n (Settled)=\"OnCanvasSettled()\"\n (Frame)=\"OnFrame($event)\"\n (PausedChange)=\"DebugPaused = $event\">\n </mj-task-graph-debugger>\n\n @if (CanReplay) {\n <!-- Replay: scrub through the settled run on the same canvas, reconstructed\n from each step's own timestamps. Read-only by construction. -->\n <div class=\"wfr-replay\">\n <i class=\"fa-solid fa-clock-rotate-left\" aria-hidden=\"true\"></i>\n <input type=\"range\" min=\"0\" max=\"100\" step=\"1\"\n [value]=\"ReplayPercent ?? 100\"\n (input)=\"OnReplayScrub($any($event.target).value)\"\n aria-label=\"Replay position\" />\n @if (ReplayPercent !== null) {\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"OnReplayReset()\">Now</button>\n }\n </div>\n }\n\n @if (GraphDebug.Breakpoints.length > 0 || GraphDebug.PausedAtTaskID) {\n <div class=\"wfr-bp-bar\" aria-label=\"Armed breakpoints\">\n @if (GraphDebug.PausedAtTaskID) {\n <span class=\"wfr-chip wfr-chip-paused\">\n <i class=\"fa-solid fa-pause\" aria-hidden=\"true\"></i>\n Paused at {{ StepName(GraphDebug.PausedAtTaskID) }}\n </span>\n }\n @for (id of GraphDebug.Breakpoints; track id) {\n <button type=\"button\" class=\"wfr-chip wfr-chip-break\" (click)=\"OnSelectBreakpoint(id)\">\n <i class=\"fa-solid fa-circle\" aria-hidden=\"true\"></i>\n {{ StepName(id) }}\n <span class=\"wfr-chip-x\" (click)=\"OnRemoveBreakpoint(id); $event.stopPropagation()\"\n role=\"button\" tabindex=\"0\" aria-label=\"Remove breakpoint\">\u00D7</span>\n </button>\n }\n </div>\n }\n\n @if (EngineTicks.length > 0) {\n <!-- The engine strip: one tick per dispatcher pass. A stuck run is a strip\n that keeps ticking with nothing moving \u2014 the honest visual of a stall. -->\n <div class=\"wfr-engine\" aria-label=\"Engine activity\">\n @for (tick of EngineTicks; track tick.passNumber) {\n <span class=\"wfr-tick\" [class.wfr-tick-held]=\"(tick.heldCount ?? 0) > 0\">\n #{{ tick.passNumber }} \u00B7 ready {{ tick.eligibleCount ?? 0 }}\n \u00B7 held {{ tick.heldCount ?? 0 }} \u00B7 took {{ tick.claimedCount ?? 0 }}\n </span>\n }\n @if (GraphDebug.Paused) {\n <span class=\"wfr-tick wfr-tick-paused\">\u23F8 paused \u2014 nothing new starts</span>\n }\n </div>\n }\n </div>\n </as-split-area>\n\n <as-split-area [size]=\"Layout.StepPanelOpen ? Layout.StepSplitSizes[1] : 0\" [minSize]=\"20\"\n [visible]=\"Layout.StepPanelOpen\">\n <div class=\"wfr-steps-pane\">\n @if (StepsLoading) {\n <mj-loading text=\"Loading steps...\"></mj-loading>\n } @else {\n <div class=\"wfr-step-list\">\n @for (step of SelectedSteps; track step.ID) {\n <button type=\"button\" class=\"wfr-step\"\n [class.wfr-step-selected]=\"step.ID === SelectedStepID\"\n [attr.data-status]=\"step.Status\"\n (click)=\"OnSelectStep(step)\">\n <span class=\"wfr-step-name\">{{ step.Name }}</span>\n <span class=\"wfr-step-meta\">{{ step.StepType || 'Step' }}</span>\n </button>\n }\n </div>\n\n @if (InspectorMode === 'edge' && SelectedEdge; as edge) {\n <div class=\"wfr-step-json\">\n <div class=\"wfr-step-json-head\">\n <span>{{ SelectedEdgeFromName }} \u2192 {{ SelectedEdgeToName }}</span>\n @if (SelectedEdgeOverride) {\n <span class=\"wfr-chip wfr-chip-forced\">forced {{ SelectedEdgeOverride }}</span>\n }\n </div>\n <div class=\"wfr-insp-card\">\n <span class=\"wfr-insp-label\">Condition</span>\n @if (edge.Condition?.trim()) {\n <code>{{ edge.Condition }}</code>\n } @else {\n <span>Unconditional \u2014 this path always runs.</span>\n }\n </div>\n @if (SelectedEdgeIsConditional) {\n <div class=\"wfr-insp-actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(edge.ID, 'true')\">Answer true</button>\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(edge.ID, 'false')\">Answer false</button>\n @if (SelectedEdgeOverride) {\n <button mjButton variant=\"flat\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(edge.ID, null)\">Clear override</button>\n }\n </div>\n } @else {\n <p class=\"wfr-insp-hint\">Skip the target step instead of overriding an unconditional path.</p>\n }\n </div>\n } @else if (SelectedStep; as step) {\n <div class=\"wfr-step-json\">\n <div class=\"wfr-step-json-head\">\n <span>{{ step.Name }}</span>\n <span class=\"wfr-row-status\" [attr.data-status]=\"step.Status\">{{ step.Status }}</span>\n </div>\n\n @if (SelectedStepFailureReason; as reason) {\n <div class=\"wfr-insp-card wfr-insp-failure\" role=\"status\">\n <span class=\"wfr-insp-label\">Why it failed</span>\n <span>{{ reason }}</span>\n </div>\n }\n\n <button type=\"button\" class=\"wfr-bp\"\n [class.wfr-bp-on]=\"HasBreakpoint(step.ID)\"\n [disabled]=\"ControlBusy\"\n (click)=\"OnBreakpointToggled({ TaskID: step.ID, Enabled: !HasBreakpoint(step.ID) })\"\n [attr.aria-pressed]=\"HasBreakpoint(step.ID)\"\n [title]=\"HasBreakpoint(step.ID) ? 'Remove breakpoint' : 'Break before this step'\">\n <i class=\"fa-circle\"\n [class.fa-solid]=\"HasBreakpoint(step.ID)\"\n [class.fa-regular]=\"!HasBreakpoint(step.ID)\"\n aria-hidden=\"true\"></i>\n </button>\n\n <!-- The inspector: live engine facts about this step, from the frame\n stream \u2014 claim, path verdicts, runner progress, what-if. -->\n @if (SelectedStepProgress; as progress) {\n <div class=\"wfr-insp-card\">\n <span class=\"wfr-insp-label\">Doing now</span>\n <span>{{ progress.progressMessage }}\n @if (progress.progressPercent != null) { ({{ progress.progressPercent }}%) }\n </span>\n </div>\n }\n @if (SelectedStepClaim; as claim) {\n <div class=\"wfr-insp-card\" [class.wfr-insp-bad]=\"claim.claimEvent === 'heartbeat-lost'\">\n <span class=\"wfr-insp-label\">Worker</span>\n <span>\n @switch (claim.claimEvent) {\n @case ('claimed') { {{ claim.claimedBy }} \u2014 working }\n @case ('heartbeat-lost') { {{ claim.claimedBy }} \u2014 lost, will be requeued }\n @case ('reclaimed') { requeued by the engine }\n }\n </span>\n </div>\n }\n @for (verdict of SelectedStepVerdicts; track verdict.edgeId) {\n <div class=\"wfr-insp-card\" [attr.data-verdict]=\"verdict.verdict\">\n <span class=\"wfr-insp-label\">\n @switch (verdict.verdict) {\n @case ('satisfied') { Path taken }\n @case ('notTaken') { Path not taken }\n @case ('held') { Path can't be answered }\n }\n </span>\n <span>\n @if (verdict.conditionText) { <code>{{ verdict.conditionText }}</code> }\n @if (verdict.reason) { \u2014 {{ verdict.reason }} }\n </span>\n </div>\n }\n @if (SelectedStepWhatIf; as whatIf) {\n <div class=\"wfr-insp-card\">\n <span class=\"wfr-insp-label\">If this step fails</span>\n <span>{{ whatIf.BlockedCount }} step{{ whatIf.BlockedCount === 1 ? '' : 's' }} can no longer run</span>\n </div>\n }\n @if (step.Status === 'Pending' || step.Status === 'Failed' || CanForceComplete(step)) {\n <div class=\"wfr-insp-actions\">\n @if (step.Status === 'Pending') {\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnSkipStep(step)\"\n title=\"Declare this step not-taken; later steps proceed\">\n <i class=\"fa-solid fa-forward\" aria-hidden=\"true\"></i> Skip step\n </button>\n }\n @if (CanEditInput(step)) {\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OpenEditInput(step)\">\n <i class=\"fa-solid fa-pen-to-square\" aria-hidden=\"true\"></i>\n {{ step.Status === 'Failed' ? 'Edit input & retry' : 'Edit input' }}\n </button>\n }\n @if (step.Status === 'Failed') {\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnRetryStep(step)\">\n <i class=\"fa-solid fa-rotate-right\" aria-hidden=\"true\"></i> Retry\n </button>\n }\n @if (CanForceComplete(step)) {\n <button mjButton variant=\"danger\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OpenForceComplete(step)\">\n <i class=\"fa-solid fa-flag-checkered\" aria-hidden=\"true\"></i> Force complete\n </button>\n }\n </div>\n }\n\n @if (EditInputOpen) {\n <div class=\"wfr-editor-card\">\n <span class=\"wfr-insp-label\">{{ step.Status === 'Failed' ? 'Edit input and retry' : 'Edit input' }}</span>\n <p class=\"wfr-insp-hint\">Applies to this run only. Invalid JSON is refused here.</p>\n <textarea class=\"mj-textarea wfr-json-edit\" [(ngModel)]=\"EditInputJson\"\n rows=\"8\" spellcheck=\"false\"></textarea>\n @if (EditInputError) {\n <span class=\"wfr-editor-error\">{{ EditInputError }}</span>\n }\n <div class=\"wfr-insp-actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"SubmitEditInput(step)\">\n {{ step.Status === 'Failed' ? 'Apply & retry' : 'Apply' }}\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"EditInputOpen = false\">Cancel</button>\n </div>\n </div>\n }\n\n @if (ForceCompleteOpen) {\n <div class=\"wfr-editor-card wfr-editor-danger\">\n <span class=\"wfr-insp-label\">Force complete</span>\n <p class=\"wfr-insp-hint\">Downstream conditions will evaluate against this output as if the step had produced it. Type the step name to confirm.</p>\n <textarea class=\"mj-textarea wfr-json-edit\" [(ngModel)]=\"ForceCompleteJson\"\n rows=\"6\" spellcheck=\"false\"></textarea>\n <input class=\"mj-input\" type=\"text\" [(ngModel)]=\"ForceCompleteName\"\n [placeholder]=\"'Type \u201C' + step.Name + '\u201D to confirm'\" />\n @if (ForceCompleteError) {\n <span class=\"wfr-editor-error\">{{ ForceCompleteError }}</span>\n }\n <div class=\"wfr-insp-actions\">\n <button mjButton variant=\"danger\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"SubmitForceComplete(step)\">Force complete</button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"ForceCompleteOpen = false\">Cancel</button>\n </div>\n </div>\n }\n\n <mj-task-graph-variables\n [InputPayload]=\"StepPayload(step, 'InputPayload')\"\n [OutputPayload]=\"StepPayload(step, 'OutputPayload')\"\n [Invocation]=\"Invocation\">\n </mj-task-graph-variables>\n </div>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-hand-pointer\"\n Title=\"Select a step\"\n Message=\"Click a step on the canvas or in the list to see everything it recorded.\">\n </mj-empty-state>\n }\n }\n </div>\n </as-split-area>\n </as-split>\n } @else {\n <div class=\"wfr-json-pane\">\n <mj-code-editor\n [ngModel]=\"SelectedRunJson\"\n [language]=\"'json'\"\n [readonly]=\"true\"\n style=\"width: 100%;\">\n </mj-code-editor>\n </div>\n }\n </aside>\n }\n </as-split-area>\n </as-split>\n }\n </mj-page-body>\n</mj-page-layout>\n", styles: [":host {\n display: block;\n height: 100%;\n}\n\n.wfr-status-filters {\n display: flex;\n gap: var(--mj-space-1);\n align-items: center;\n}\n\n/* List alone until something is selected; two resizable panes after. The size is the user's \u2014 see\n the LAYOUT_KEYS note on the component for why it is persisted server-side rather than locally. */\n.wfr-split {\n height: 100%;\n min-height: 0;\n}\n\n/* A gutter you can find. The default is a hairline, which is discoverable only by accident. */\n:host ::ng-deep .wfr-split > .as-split-gutter,\n:host ::ng-deep .wfr-graph-split > .as-split-gutter {\n background: transparent !important;\n position: relative;\n}\n\n:host ::ng-deep .wfr-split > .as-split-gutter::after,\n:host ::ng-deep .wfr-graph-split > .as-split-gutter::after {\n content: '';\n position: absolute;\n inset: 0;\n margin: auto;\n background: var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n}\n\n:host ::ng-deep .wfr-split.as-horizontal > .as-split-gutter::after,\n:host ::ng-deep .wfr-graph-split.as-horizontal > .as-split-gutter::after {\n width: 2px;\n}\n\n:host ::ng-deep .wfr-graph-split.as-vertical > .as-split-gutter::after {\n height: 2px;\n}\n\n:host ::ng-deep .wfr-split > .as-split-gutter:hover::after,\n:host ::ng-deep .wfr-graph-split > .as-split-gutter:hover::after {\n background: var(--mj-brand-primary);\n}\n\n/* The canvas and the step record sit SIDE BY SIDE \u2014 a JSON record in a strip along the bottom shows\n about ten lines. Stacks below the breakpoint, where three columns would leave each too narrow to\n use. */\n.wfr-graph-split {\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.wfr-list {\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-1);\n overflow-y: auto;\n min-height: 0;\n}\n\n.wfr-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--mj-space-2);\n width: 100%;\n padding: var(--mj-space-2) var(--mj-space-3);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n text-align: left;\n cursor: pointer;\n color: var(--mj-text-primary);\n}\n\n.wfr-row:hover {\n border-color: var(--mj-border-strong);\n}\n\n.wfr-row:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.wfr-row-selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.wfr-row-main {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.wfr-row-name {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wfr-row-when {\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n}\n\n.wfr-row-status {\n flex: none;\n font-size: var(--mj-font-size-xs);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n padding: 2px var(--mj-space-2);\n border-radius: var(--mj-radius-full);\n background: var(--mj-bg-page);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n}\n\n/* Status carries meaning, so it is encoded in colour as well as text \u2014 a failed run should be\n findable without reading every row. Skipped stays grey on purpose: a branch that was not taken\n is a normal outcome, not a warning. */\n.wfr-row-status[data-status='Complete'] {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 40%, transparent);\n}\n\n.wfr-row-status[data-status='Failed'] {\n color: var(--mj-status-error);\n border-color: color-mix(in srgb, var(--mj-status-error) 40%, transparent);\n}\n\n.wfr-row-status[data-status='Running'] {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.wfr-detail {\n display: flex;\n flex-direction: column;\n /* Fills its split area. Without it the column sizes to its CONTENT, so the canvas below \u2014\n `flex: 1 1 auto` of an auto-height parent \u2014 got its intrinsic height and left dead space. */\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n}\n\n.wfr-detail-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.wfr-detail-title {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n min-width: 0;\n}\n\n.wfr-detail-name {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wfr-detail-actions {\n display: flex;\n align-items: center;\n gap: var(--mj-space-1);\n flex: none;\n}\n\n/* ============================================================\n The run list as a sortable table\n ============================================================\n A run list is scanned for two things \u2014 the slowest run and the one that failed \u2014 and neither is\n answerable by scrolling. Columns line up so a reader compares down a column rather than reading\n each row; `tabular-nums` keeps the timestamps from jittering as digits change. */\n.wfr-head,\n.wfr-row {\n display: grid;\n grid-template-columns: minmax(0, 2fr) minmax(0, 1.3fr) minmax(0, 1.3fr) minmax(0, 0.7fr) minmax(0, 0.9fr);\n gap: var(--mj-space-2);\n align-items: center;\n}\n\n.wfr-head {\n position: sticky;\n top: 0;\n z-index: 1;\n padding: 0 var(--mj-space-3) var(--mj-space-1);\n background: var(--mj-bg-page);\n}\n\n.wfr-head-cell {\n display: flex;\n align-items: center;\n gap: 6px;\n background: none;\n border: none;\n padding: var(--mj-space-1) 0;\n font-size: var(--mj-font-size-xs);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n cursor: pointer;\n text-align: left;\n}\n\n.wfr-head-cell:hover {\n color: var(--mj-text-primary);\n}\n\n.wfr-head-cell i {\n opacity: 0.65;\n font-size: 0.85em;\n}\n\n.wfr-col-time,\n.wfr-col-duration {\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.wfr-col-status {\n justify-self: start;\n}\n\n/* ============================================================\n Detail panel: canvas + step JSON, or the whole run as JSON\n ============================================================ */\n.wfr-detail-tabs {\n display: flex;\n gap: var(--mj-space-1);\n padding: 0 var(--mj-space-3);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.wfr-tab {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n padding: var(--mj-space-2) var(--mj-space-2);\n font-size: var(--mj-font-size-sm);\n font-weight: 600;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.wfr-tab-active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.wfr-graph-pane {\n height: 100%;\n min-height: 12rem;\n}\n\n.wfr-steps-pane {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n border-left: 1px solid var(--mj-border-default);\n overflow: hidden;\n}\n\n/* Stacked layout puts the divider back on top, where the eye expects it. */\n:host ::ng-deep .as-vertical .wfr-steps-pane {\n border-left: none;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.wfr-step-list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--mj-space-1);\n padding: var(--mj-space-2);\n border-bottom: 1px solid var(--mj-border-default);\n max-height: 8rem;\n overflow-y: auto;\n flex: none;\n}\n\n.wfr-step {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-1) var(--mj-space-2);\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-primary);\n font-size: var(--mj-font-size-sm);\n cursor: pointer;\n text-align: left;\n}\n\n.wfr-step:hover {\n border-color: var(--mj-border-strong);\n}\n\n.wfr-step-selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n/* Work that never ran must not read like work that did \u2014 the same rule the run timeline follows. */\n.wfr-step[data-status='Skipped'] .wfr-step-name {\n text-decoration: line-through;\n color: var(--mj-text-secondary);\n}\n\n.wfr-step-name {\n font-weight: 600;\n}\n\n.wfr-step-meta {\n font-size: var(--mj-font-size-xs);\n color: var(--mj-text-secondary);\n}\n\n.wfr-step-json,\n.wfr-json-pane {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: auto;\n}\n\n.wfr-step-json-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--mj-space-2);\n padding: var(--mj-space-1) var(--mj-space-3);\n font-weight: 600;\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-secondary);\n flex: none;\n}\n\n/* \u2500\u2500 live console \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.wfr-stall {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 12px;\n margin: 0 12px 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 45%, var(--mj-border-default));\n border-left: 4px solid var(--mj-status-warning);\n border-radius: var(--mj-radius-md);\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n font-size: var(--mj-font-size-sm);\n}\n.wfr-stall-body {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n.wfr-stall i { color: var(--mj-status-warning); }\n.wfr-stall-copy {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n.wfr-stall-copy code {\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n font-size: var(--mj-font-size-xs);\n}\n.wfr-stall-reason { color: var(--mj-text-secondary); }\n.wfr-stall-actions { display: flex; flex-wrap: wrap; gap: 6px; }\n\n.wfr-replay {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 12px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n}\n.wfr-replay i { color: var(--mj-text-secondary); }\n.wfr-replay input[type='range'] { flex: 1; accent-color: var(--mj-brand-primary); }\n\n.wfr-engine {\n display: flex;\n gap: 8px;\n overflow-x: auto;\n padding: 6px 12px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n font-size: var(--mj-font-size-xs);\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n}\n.wfr-tick {\n flex: none;\n padding: 3px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface);\n white-space: nowrap;\n}\n.wfr-tick-held {\n border-color: color-mix(in srgb, var(--mj-status-warning, #d97706) 45%, var(--mj-border-default));\n background: color-mix(in srgb, var(--mj-status-warning, #d97706) 8%, var(--mj-bg-surface));\n}\n.wfr-tick-paused { color: var(--mj-brand-primary); border-color: var(--mj-border-strong); }\n\n.wfr-insp-card {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 7px 10px;\n margin: 0 0 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface);\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-primary);\n}\n.wfr-insp-card code {\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n font-size: var(--mj-font-size-xs);\n}\n.wfr-insp-label {\n font-size: var(--mj-font-size-xs);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n}\n.wfr-insp-card[data-verdict='held'],\n.wfr-insp-bad {\n border-color: color-mix(in srgb, var(--mj-status-warning, #d97706) 45%, var(--mj-border-default));\n background: color-mix(in srgb, var(--mj-status-warning, #d97706) 8%, var(--mj-bg-surface));\n}\n.wfr-insp-card[data-verdict='satisfied'] .wfr-insp-label { color: var(--mj-status-success); }\n.wfr-insp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 8px; }\n.wfr-insp-hint {\n margin: 0 0 8px;\n font-size: var(--mj-font-size-xs);\n color: var(--mj-text-muted);\n}\n\n.wfr-bp-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 6px 12px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n}\n.wfr-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: var(--mj-font-size-xs);\n cursor: pointer;\n}\n.wfr-chip-break { color: var(--mj-status-error); border-color: color-mix(in srgb, var(--mj-status-error) 40%, transparent); }\n.wfr-chip-break i { font-size: 7px; }\n.wfr-chip-paused {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n cursor: default;\n}\n.wfr-chip-forced {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 40%, transparent);\n cursor: default;\n}\n.wfr-chip-x {\n font-size: 14px;\n line-height: 1;\n padding: 0 2px;\n}\n.wfr-chip-x:hover { color: var(--mj-text-primary); }\n\n.wfr-bp {\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n margin: 0 0 8px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n}\n.wfr-bp:hover { color: var(--mj-status-error); }\n.wfr-bp-on,\n.wfr-bp-on:hover { color: var(--mj-status-error); }\n.wfr-bp i { font-size: 10px; }\n.wfr-bp:disabled { opacity: 0.45; cursor: default; }\n\n.wfr-editor-card {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n margin: 0 0 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface-card);\n}\n.wfr-editor-danger {\n border-color: color-mix(in srgb, var(--mj-status-error) 40%, var(--mj-border-default));\n}\n.wfr-json-edit {\n width: 100%;\n min-height: 120px;\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n font-size: var(--mj-font-size-xs);\n resize: vertical;\n}\n.wfr-editor-error { color: var(--mj-status-error); font-size: var(--mj-font-size-xs); }\n.wfr-insp-failure { border-color: var(--mj-status-error); color: var(--mj-status-error); }\n"] }]
1834
+ args: [{ standalone: false, selector: 'mj-workflow-runs', changeDetection: ChangeDetectionStrategy.OnPush, template: "<mj-page-layout>\n <mj-page-header\n Title=\"Runs\"\n Icon=\"fa-solid fa-timeline\"\n Subtitle=\"Every workflow that has run \u2014 however it was started.\">\n\n <div meta>\n @if (!IsLoading && Runs.length > 0) {\n <mj-stat-badge\n [Count]=\"VisibleRuns.length\"\n [Total]=\"Runs.length\"\n Label=\"runs\"></mj-stat-badge>\n }\n </div>\n\n <div actions>\n <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"loadData()\"></mj-refresh-button>\n </div>\n\n <div toolbar>\n <mj-page-search\n [Value]=\"SearchText\"\n Placeholder=\"Search runs\"\n (ValueChange)=\"OnSearch($event)\"></mj-page-search>\n\n <div class=\"wfr-status-filters\">\n @for (status of StatusFilters; track status) {\n <button\n type=\"button\"\n mjButton\n [variant]=\"StatusFilter === status ? 'primary' : 'flat'\"\n size=\"sm\"\n (click)=\"OnFilterByStatus(status)\">\n {{ status === 'all' ? 'All' : status }}\n </button>\n }\n </div>\n </div>\n </mj-page-header>\n\n <mj-page-body>\n @if (IsLoading) {\n <mj-loading text=\"Loading runs...\"></mj-loading>\n } @else if (LoadError) {\n <mj-alert Variant=\"error\" [Message]=\"LoadError\"></mj-alert>\n } @else if (Runs.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-timeline\"\n Title=\"Nothing has run yet\"\n Message=\"When a workflow runs \u2014 started by hand, on a schedule, or by an agent \u2014 it will appear here.\">\n </mj-empty-state>\n } @else if (VisibleRuns.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-filter\"\n Title=\"No runs match\"\n Message=\"Nothing matches the current filter. Clear it to see everything that has run.\">\n </mj-empty-state>\n } @else {\n <as-split class=\"wfr-split\" direction=\"horizontal\" unit=\"percent\"\n [gutterSize]=\"8\" [disabled]=\"!SelectedRun\" (dragEnd)=\"OnSplitDragEnd($event.sizes)\">\n <as-split-area [size]=\"SelectedRun ? Layout.SplitSizes[0] : 100\" [minSize]=\"20\">\n <div class=\"wfr-list\">\n <!-- A header row that sorts. Every column a person scans by is sortable, because \"the\n longest run\" and \"the one that failed this morning\" are the two questions this list\n exists to answer and neither is answerable by scrolling. -->\n <div class=\"wfr-head\" role=\"row\">\n <button type=\"button\" class=\"wfr-head-cell wfr-col-name\" (click)=\"OnSort('Name')\">\n Workflow <i [ngClass]=\"SortIcon('Name')\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"wfr-head-cell wfr-col-time\" (click)=\"OnSort('StartedAt')\">\n Started <i [ngClass]=\"SortIcon('StartedAt')\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"wfr-head-cell wfr-col-time\" (click)=\"OnSort('CompletedAt')\">\n Ended <i [ngClass]=\"SortIcon('CompletedAt')\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"wfr-head-cell wfr-col-duration\" (click)=\"OnSort('Duration')\">\n Duration <i [ngClass]=\"SortIcon('Duration')\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"wfr-head-cell wfr-col-status\" (click)=\"OnSort('Status')\">\n Status <i [ngClass]=\"SortIcon('Status')\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @for (run of VisibleRuns; track run.ID) {\n <button\n type=\"button\"\n class=\"wfr-row\"\n [class.wfr-row-selected]=\"run.ID === SelectedRunID\"\n (click)=\"OnSelectRun(run)\">\n <span class=\"wfr-col-name wfr-row-name\">{{ run.Name }}</span>\n <!-- An em dash, never a fabricated time: a run that has not started has no start, and\n printing one would read as data. -->\n <span class=\"wfr-col-time\">{{ run.StartedAt ? (run.StartedAt | date:'MMM d, h:mm:ss a') : '\u2014' }}</span>\n <span class=\"wfr-col-time\">{{ run.CompletedAt ? (run.CompletedAt | date:'MMM d, h:mm:ss a') : '\u2014' }}</span>\n <span class=\"wfr-col-duration\">{{ run.Duration }}</span>\n <span class=\"wfr-col-status wfr-row-status\" [attr.data-status]=\"run.Status\">{{ run.Status }}</span>\n </button>\n }\n </div>\n </as-split-area>\n\n <as-split-area [size]=\"SelectedRun ? Layout.SplitSizes[1] : 0\" [minSize]=\"25\" [visible]=\"!!SelectedRun\">\n @if (SelectedRun; as selected) {\n <!-- The canvas lives in the RIGHT PANEL on selection, not nested inside the list row.\n A graph needs room to be read; inlining one per row turns a scannable list into a\n stack of diagrams. -->\n <aside class=\"wfr-detail\">\n <header class=\"wfr-detail-head\">\n <div class=\"wfr-detail-title\">\n <span class=\"wfr-detail-name\">{{ selected.Name }}</span>\n <span class=\"wfr-row-status\" [attr.data-status]=\"selected.Status\">{{ selected.Status }}</span>\n </div>\n <div class=\"wfr-detail-actions\">\n @if (selected.AgentRunID) {\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"OnOpenAgentRun(selected)\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\" aria-hidden=\"true\"></i>\n Agent run\n </button>\n }\n @if (DetailTab === 'graph') {\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"ToggleStepPanel()\"\n [title]=\"Layout.StepPanelOpen ? 'Hide step details' : 'Show step details'\">\n <i class=\"fa-solid\" [class.fa-square-caret-right]=\"Layout.StepPanelOpen\"\n [class.fa-square-caret-left]=\"!Layout.StepPanelOpen\" aria-hidden=\"true\"></i>\n </button>\n }\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"OnSelectRun(selected)\" aria-label=\"Close\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </header>\n\n <div class=\"wfr-detail-tabs\" role=\"tablist\">\n <button type=\"button\" class=\"wfr-tab\" [class.wfr-tab-active]=\"DetailTab === 'graph'\"\n (click)=\"OnDetailTab('graph')\">\n <i class=\"fa-solid fa-diagram-project\" aria-hidden=\"true\"></i> Workflow\n </button>\n <button type=\"button\" class=\"wfr-tab\" [class.wfr-tab-active]=\"DetailTab === 'json'\"\n (click)=\"OnDetailTab('json')\">\n <i class=\"fa-solid fa-code\" aria-hidden=\"true\"></i> JSON\n </button>\n </div>\n\n @if (Stall) {\n <!-- The hardening review's signature failure \u2014 \"the run strands silently\" \u2014 made\n loud. A held path is now something the operator can answer, not just read. -->\n <div class=\"wfr-stall\" role=\"status\" [attr.data-kind]=\"Stall.kind\">\n <div class=\"wfr-stall-body\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n <div class=\"wfr-stall-copy\">\n <span>{{ Stall.message }}</span>\n @if (Stall.conditionText) {\n <code>{{ Stall.conditionText }}</code>\n }\n @if (Stall.reason) {\n <span class=\"wfr-stall-reason\">{{ Stall.reason }}</span>\n }\n </div>\n </div>\n @if (Stall.kind === 'held' && Stall.edgeID) {\n <div class=\"wfr-stall-actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(Stall.edgeID!, 'true')\">\n Answer true\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(Stall.edgeID!, 'false')\">\n Answer false\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"OnDismissStall()\">\n Leave held\n </button>\n </div>\n }\n </div>\n }\n\n @if (DetailTab === 'graph') {\n <!-- Canvas BESIDE the step it opened, not above it. A JSON record in a strip along the\n bottom shows about ten lines, which is not enough of a record to read. -->\n <as-split class=\"wfr-graph-split\" [direction]=\"InnerSplitDirection\" unit=\"percent\"\n [gutterSize]=\"8\" (dragEnd)=\"OnStepSplitDragEnd($event.sizes)\">\n <as-split-area [size]=\"Layout.StepPanelOpen ? Layout.StepSplitSizes[0] : 100\" [minSize]=\"25\">\n <div class=\"wfr-graph-pane\">\n <mj-task-graph-debugger\n #GraphDebug\n [Provider]=\"ProviderToUse\"\n [ParentTaskID]=\"selected.ID\"\n [WorkflowName]=\"selected.Name\"\n [LiveUpdates]=\"selected.Status === 'Running' || selected.Status === 'In Progress'\"\n [ReplayAt]=\"ReplayAt\"\n [ShowLegend]=\"Layout.ShowLegend\"\n [ShowVariables]=\"false\"\n Height=\"100%\"\n (NodeSelected)=\"OnGraphNodeSelected($event)\"\n (ConnectionSelected)=\"OnGraphConnectionSelected($event)\"\n (LegendToggled)=\"OnLegendToggled($event)\"\n (Settled)=\"OnCanvasSettled()\"\n (Frame)=\"OnFrame($event)\"\n (PausedChange)=\"DebugPaused = $event\">\n </mj-task-graph-debugger>\n\n @if (CanReplay) {\n <!-- Replay: scrub through the settled run on the same canvas, reconstructed\n from each step's own timestamps. Read-only by construction. -->\n <div class=\"wfr-replay\">\n <i class=\"fa-solid fa-clock-rotate-left\" aria-hidden=\"true\"></i>\n <input type=\"range\" min=\"0\" max=\"100\" step=\"1\"\n [value]=\"ReplayPercent ?? 100\"\n (input)=\"OnReplayScrub($any($event.target).value)\"\n aria-label=\"Replay position\" />\n @if (ReplayPercent !== null) {\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"OnReplayReset()\">Now</button>\n }\n </div>\n }\n\n @if (GraphDebug.Breakpoints.length > 0 || GraphDebug.PausedAtTaskID) {\n <div class=\"wfr-bp-bar\" aria-label=\"Armed breakpoints\">\n @if (GraphDebug.PausedAtTaskID) {\n <span class=\"wfr-chip wfr-chip-paused\">\n <i class=\"fa-solid fa-pause\" aria-hidden=\"true\"></i>\n Paused at {{ StepName(GraphDebug.PausedAtTaskID) }}\n </span>\n }\n @for (id of GraphDebug.Breakpoints; track id) {\n <button type=\"button\" class=\"wfr-chip wfr-chip-break\" (click)=\"OnSelectBreakpoint(id)\">\n <i class=\"fa-solid fa-circle\" aria-hidden=\"true\"></i>\n {{ StepName(id) }}\n <span class=\"wfr-chip-x\" (click)=\"OnRemoveBreakpoint(id); $event.stopPropagation()\"\n role=\"button\" tabindex=\"0\" aria-label=\"Remove breakpoint\">\u00D7</span>\n </button>\n }\n </div>\n }\n\n @if (EngineTicks.length > 0) {\n <!-- The engine strip: one tick per dispatcher pass. A stuck run is a strip\n that keeps ticking with nothing moving \u2014 the honest visual of a stall. -->\n <div class=\"wfr-engine\" aria-label=\"Engine activity\">\n @for (tick of EngineTicks; track tick.passNumber) {\n <span class=\"wfr-tick\" [class.wfr-tick-held]=\"(tick.heldCount ?? 0) > 0\">\n #{{ tick.passNumber }} \u00B7 ready {{ tick.eligibleCount ?? 0 }}\n \u00B7 held {{ tick.heldCount ?? 0 }} \u00B7 took {{ tick.claimedCount ?? 0 }}\n </span>\n }\n @if (GraphDebug.Paused) {\n <span class=\"wfr-tick wfr-tick-paused\">\u23F8 paused \u2014 nothing new starts</span>\n }\n </div>\n }\n </div>\n </as-split-area>\n\n <as-split-area [size]=\"Layout.StepPanelOpen ? Layout.StepSplitSizes[1] : 0\" [minSize]=\"20\"\n [visible]=\"Layout.StepPanelOpen\">\n <div class=\"wfr-steps-pane\">\n @if (StepsLoading) {\n <mj-loading text=\"Loading steps...\"></mj-loading>\n } @else {\n <div class=\"wfr-step-list\">\n @for (step of SelectedSteps; track step.ID) {\n <button type=\"button\" class=\"wfr-step\"\n [class.wfr-step-selected]=\"step.ID === SelectedStepID\"\n [attr.data-status]=\"step.Status\"\n (click)=\"OnSelectStep(step)\">\n <span class=\"wfr-step-name\">{{ step.Name }}</span>\n <span class=\"wfr-step-meta\">{{ step.StepType || 'Step' }}</span>\n </button>\n }\n </div>\n\n @if (InspectorMode === 'edge' && SelectedEdge; as edge) {\n <div class=\"wfr-step-json\">\n <div class=\"wfr-step-json-head\">\n <span>{{ SelectedEdgeFromName }} \u2192 {{ SelectedEdgeToName }}</span>\n @if (SelectedEdgeOverride) {\n <span class=\"wfr-chip wfr-chip-forced\">forced {{ SelectedEdgeOverride }}</span>\n }\n </div>\n <div class=\"wfr-insp-card\">\n <span class=\"wfr-insp-label\">Condition</span>\n @if (edge.Condition?.trim()) {\n <code>{{ edge.Condition }}</code>\n } @else {\n <span>Unconditional \u2014 this path always runs.</span>\n }\n </div>\n @if (SelectedEdgeIsConditional) {\n <div class=\"wfr-insp-actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(edge.ID, 'true')\">Answer true</button>\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(edge.ID, 'false')\">Answer false</button>\n @if (SelectedEdgeOverride) {\n <button mjButton variant=\"flat\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnOverrideEdge(edge.ID, null)\">Clear override</button>\n }\n </div>\n } @else {\n <p class=\"wfr-insp-hint\">Skip the target step instead of overriding an unconditional path.</p>\n }\n </div>\n } @else if (SelectedStep; as step) {\n <div class=\"wfr-step-json\">\n <div class=\"wfr-step-json-head\">\n <span>{{ step.Name }}</span>\n <span class=\"wfr-row-status\" [attr.data-status]=\"step.Status\">{{ step.Status }}</span>\n </div>\n\n @if (SelectedStepFailureReason; as reason) {\n <div class=\"wfr-insp-card wfr-insp-failure\" role=\"status\">\n <span class=\"wfr-insp-label\">Why it failed</span>\n <span>{{ reason }}</span>\n </div>\n }\n\n <button type=\"button\" class=\"wfr-bp\"\n [class.wfr-bp-on]=\"HasBreakpoint(step.ID)\"\n [disabled]=\"ControlBusy\"\n (click)=\"OnBreakpointToggled({ TaskID: step.ID, Enabled: !HasBreakpoint(step.ID) })\"\n [attr.aria-pressed]=\"HasBreakpoint(step.ID)\"\n [title]=\"HasBreakpoint(step.ID) ? 'Remove breakpoint' : 'Break before this step'\">\n <i class=\"fa-circle\"\n [class.fa-solid]=\"HasBreakpoint(step.ID)\"\n [class.fa-regular]=\"!HasBreakpoint(step.ID)\"\n aria-hidden=\"true\"></i>\n </button>\n\n <!-- The inspector: live engine facts about this step, from the frame\n stream \u2014 claim, path verdicts, runner progress, what-if. -->\n @if (SelectedStepProgress; as progress) {\n <div class=\"wfr-insp-card\">\n <span class=\"wfr-insp-label\">Doing now</span>\n <span>{{ progress.progressMessage }}\n @if (progress.progressPercent != null) { ({{ progress.progressPercent }}%) }\n </span>\n </div>\n }\n @if (SelectedStepClaim; as claim) {\n <div class=\"wfr-insp-card\" [class.wfr-insp-bad]=\"claim.claimEvent === 'heartbeat-lost'\">\n <span class=\"wfr-insp-label\">Worker</span>\n <span>\n @switch (claim.claimEvent) {\n @case ('claimed') { {{ claim.claimedBy }} \u2014 working }\n @case ('heartbeat-lost') { {{ claim.claimedBy }} \u2014 lost, will be requeued }\n @case ('reclaimed') { requeued by the engine }\n }\n </span>\n </div>\n }\n @for (verdict of SelectedStepVerdicts; track verdict.edgeId) {\n <div class=\"wfr-insp-card\" [attr.data-verdict]=\"verdict.verdict\">\n <span class=\"wfr-insp-label\">\n @switch (verdict.verdict) {\n @case ('satisfied') { Path taken }\n @case ('notTaken') { Path not taken }\n @case ('held') { Path can't be answered }\n }\n </span>\n <span>\n @if (verdict.conditionText) { <code>{{ verdict.conditionText }}</code> }\n @if (verdict.reason) { \u2014 {{ verdict.reason }} }\n </span>\n </div>\n }\n @if (SelectedStepWhatIf; as whatIf) {\n <div class=\"wfr-insp-card\">\n <span class=\"wfr-insp-label\">If this step fails</span>\n <span>{{ whatIf.BlockedCount }} step{{ whatIf.BlockedCount === 1 ? '' : 's' }} can no longer run</span>\n </div>\n }\n @if (step.Status === 'Pending' || step.Status === 'Failed' || CanForceComplete(step)) {\n <div class=\"wfr-insp-actions\">\n @if (step.Status === 'Pending') {\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnSkipStep(step)\"\n title=\"Declare this step not-taken; later steps proceed\">\n <i class=\"fa-solid fa-forward\" aria-hidden=\"true\"></i> Skip step\n </button>\n }\n @if (CanEditInput(step)) {\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OpenEditInput(step)\">\n <i class=\"fa-solid fa-pen-to-square\" aria-hidden=\"true\"></i>\n {{ step.Status === 'Failed' ? 'Edit input & retry' : 'Edit input' }}\n </button>\n }\n @if (step.Status === 'Failed') {\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OnRetryStep(step)\">\n <i class=\"fa-solid fa-rotate-right\" aria-hidden=\"true\"></i> Retry\n </button>\n }\n @if (CanForceComplete(step)) {\n <button mjButton variant=\"danger\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"OpenForceComplete(step)\">\n <i class=\"fa-solid fa-flag-checkered\" aria-hidden=\"true\"></i> Force complete\n </button>\n }\n </div>\n }\n\n @if (EditInputOpen) {\n <div class=\"wfr-editor-card\">\n <span class=\"wfr-insp-label\">{{ step.Status === 'Failed' ? 'Edit input and retry' : 'Edit input' }}</span>\n <p class=\"wfr-insp-hint\">Applies to this run only. Invalid JSON is refused here.</p>\n <textarea class=\"mj-textarea wfr-json-edit\" [(ngModel)]=\"EditInputJson\"\n rows=\"8\" spellcheck=\"false\"></textarea>\n @if (EditInputError) {\n <span class=\"wfr-editor-error\">{{ EditInputError }}</span>\n }\n <div class=\"wfr-insp-actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"SubmitEditInput(step)\">\n {{ step.Status === 'Failed' ? 'Apply & retry' : 'Apply' }}\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"EditInputOpen = false\">Cancel</button>\n </div>\n </div>\n }\n\n @if (ForceCompleteOpen) {\n <div class=\"wfr-editor-card wfr-editor-danger\">\n <span class=\"wfr-insp-label\">Force complete</span>\n <p class=\"wfr-insp-hint\">Downstream conditions will evaluate against this output as if the step had produced it. Type the step name to confirm.</p>\n <textarea class=\"mj-textarea wfr-json-edit\" [(ngModel)]=\"ForceCompleteJson\"\n rows=\"6\" spellcheck=\"false\"></textarea>\n <input class=\"mj-input\" type=\"text\" [(ngModel)]=\"ForceCompleteName\"\n [placeholder]=\"'Type \u201C' + step.Name + '\u201D to confirm'\" />\n @if (ForceCompleteError) {\n <span class=\"wfr-editor-error\">{{ ForceCompleteError }}</span>\n }\n <div class=\"wfr-insp-actions\">\n <button mjButton variant=\"danger\" size=\"sm\" [disabled]=\"ControlBusy\"\n (click)=\"SubmitForceComplete(step)\">Force complete</button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"ForceCompleteOpen = false\">Cancel</button>\n </div>\n </div>\n }\n\n <mj-task-graph-variables\n [InputPayload]=\"StepPayload(step, 'InputPayload')\"\n [OutputPayload]=\"StepPayload(step, 'OutputPayload')\"\n [Invocation]=\"Invocation\">\n </mj-task-graph-variables>\n </div>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-hand-pointer\"\n Title=\"Select a step\"\n Message=\"Click a step on the canvas or in the list to see everything it recorded.\">\n </mj-empty-state>\n }\n }\n </div>\n </as-split-area>\n </as-split>\n } @else {\n <div class=\"wfr-json-pane\">\n <mj-code-editor\n [ngModel]=\"SelectedRunJson\"\n [language]=\"'json'\"\n [readonly]=\"true\"\n style=\"width: 100%;\">\n </mj-code-editor>\n </div>\n }\n </aside>\n }\n </as-split-area>\n </as-split>\n }\n </mj-page-body>\n</mj-page-layout>\n", styles: [":host {\n display: block;\n height: 100%;\n}\n\n.wfr-status-filters {\n display: flex;\n gap: var(--mj-space-1);\n align-items: center;\n}\n\n/* List alone until something is selected; two resizable panes after. The size is the user's \u2014 see\n the LAYOUT_KEYS note on the component for why it is persisted server-side rather than locally. */\n.wfr-split {\n height: 100%;\n min-height: 0;\n}\n\n/* A gutter you can find. The default is a hairline, which is discoverable only by accident. */\n:host ::ng-deep .wfr-split > .as-split-gutter,\n:host ::ng-deep .wfr-graph-split > .as-split-gutter {\n background: transparent !important;\n position: relative;\n}\n\n:host ::ng-deep .wfr-split > .as-split-gutter::after,\n:host ::ng-deep .wfr-graph-split > .as-split-gutter::after {\n content: '';\n position: absolute;\n inset: 0;\n margin: auto;\n background: var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n}\n\n:host ::ng-deep .wfr-split.as-horizontal > .as-split-gutter::after,\n:host ::ng-deep .wfr-graph-split.as-horizontal > .as-split-gutter::after {\n width: 2px;\n}\n\n:host ::ng-deep .wfr-graph-split.as-vertical > .as-split-gutter::after {\n height: 2px;\n}\n\n:host ::ng-deep .wfr-split > .as-split-gutter:hover::after,\n:host ::ng-deep .wfr-graph-split > .as-split-gutter:hover::after {\n background: var(--mj-brand-primary);\n}\n\n/* The canvas and the step record sit SIDE BY SIDE \u2014 a JSON record in a strip along the bottom shows\n about ten lines. Stacks below the breakpoint, where three columns would leave each too narrow to\n use. */\n.wfr-graph-split {\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.wfr-list {\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-1);\n overflow-y: auto;\n min-height: 0;\n}\n\n.wfr-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--mj-space-2);\n width: 100%;\n padding: var(--mj-space-2) var(--mj-space-3);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n text-align: left;\n cursor: pointer;\n color: var(--mj-text-primary);\n}\n\n.wfr-row:hover {\n border-color: var(--mj-border-strong);\n}\n\n.wfr-row:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.wfr-row-selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.wfr-row-main {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.wfr-row-name {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wfr-row-when {\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n}\n\n.wfr-row-status {\n flex: none;\n font-size: var(--mj-font-size-xs);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n padding: 2px var(--mj-space-2);\n border-radius: var(--mj-radius-full);\n background: var(--mj-bg-page);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n}\n\n/* Status carries meaning, so it is encoded in colour as well as text \u2014 a failed run should be\n findable without reading every row. Skipped stays grey on purpose: a branch that was not taken\n is a normal outcome, not a warning. */\n.wfr-row-status[data-status='Complete'] {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 40%, transparent);\n}\n\n.wfr-row-status[data-status='Failed'] {\n color: var(--mj-status-error);\n border-color: color-mix(in srgb, var(--mj-status-error) 40%, transparent);\n}\n\n.wfr-row-status[data-status='Running'] {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.wfr-detail {\n display: flex;\n flex-direction: column;\n /* Fills its split area. Without it the column sizes to its CONTENT, so the canvas below \u2014\n `flex: 1 1 auto` of an auto-height parent \u2014 got its intrinsic height and left dead space. */\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n}\n\n.wfr-detail-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.wfr-detail-title {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n min-width: 0;\n}\n\n.wfr-detail-name {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wfr-detail-actions {\n display: flex;\n align-items: center;\n gap: var(--mj-space-1);\n flex: none;\n}\n\n/* ============================================================\n The run list as a sortable table\n ============================================================\n A run list is scanned for two things \u2014 the slowest run and the one that failed \u2014 and neither is\n answerable by scrolling. Columns line up so a reader compares down a column rather than reading\n each row; `tabular-nums` keeps the timestamps from jittering as digits change. */\n.wfr-head,\n.wfr-row {\n display: grid;\n grid-template-columns: minmax(0, 2fr) minmax(0, 1.3fr) minmax(0, 1.3fr) minmax(0, 0.7fr) minmax(0, 0.9fr);\n gap: var(--mj-space-2);\n align-items: center;\n}\n\n.wfr-head {\n position: sticky;\n top: 0;\n z-index: 1;\n padding: 0 var(--mj-space-3) var(--mj-space-1);\n background: var(--mj-bg-page);\n}\n\n.wfr-head-cell {\n display: flex;\n align-items: center;\n gap: 6px;\n background: none;\n border: none;\n padding: var(--mj-space-1) 0;\n font-size: var(--mj-font-size-xs);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n cursor: pointer;\n text-align: left;\n}\n\n.wfr-head-cell:hover {\n color: var(--mj-text-primary);\n}\n\n.wfr-head-cell i {\n opacity: 0.65;\n font-size: 0.85em;\n}\n\n.wfr-col-time,\n.wfr-col-duration {\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.wfr-col-status {\n justify-self: start;\n}\n\n/* ============================================================\n Detail panel: canvas + step JSON, or the whole run as JSON\n ============================================================ */\n.wfr-detail-tabs {\n display: flex;\n gap: var(--mj-space-1);\n padding: 0 var(--mj-space-3);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.wfr-tab {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n padding: var(--mj-space-2) var(--mj-space-2);\n font-size: var(--mj-font-size-sm);\n font-weight: 600;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.wfr-tab-active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.wfr-graph-pane {\n height: 100%;\n min-height: 12rem;\n}\n\n.wfr-steps-pane {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n border-left: 1px solid var(--mj-border-default);\n overflow: hidden;\n}\n\n/* Stacked layout puts the divider back on top, where the eye expects it. */\n:host ::ng-deep .as-vertical .wfr-steps-pane {\n border-left: none;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.wfr-step-list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--mj-space-1);\n padding: var(--mj-space-2);\n border-bottom: 1px solid var(--mj-border-default);\n max-height: 8rem;\n overflow-y: auto;\n flex: none;\n}\n\n.wfr-step {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-1) var(--mj-space-2);\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-primary);\n font-size: var(--mj-font-size-sm);\n cursor: pointer;\n text-align: left;\n}\n\n.wfr-step:hover {\n border-color: var(--mj-border-strong);\n}\n\n.wfr-step-selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n/* Work that never ran must not read like work that did \u2014 the same rule the run timeline follows. */\n.wfr-step[data-status='Skipped'] .wfr-step-name {\n text-decoration: line-through;\n color: var(--mj-text-secondary);\n}\n\n.wfr-step-name {\n font-weight: 600;\n}\n\n.wfr-step-meta {\n font-size: var(--mj-font-size-xs);\n color: var(--mj-text-secondary);\n}\n\n.wfr-step-json,\n.wfr-json-pane {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: auto;\n}\n\n.wfr-step-json-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--mj-space-2);\n padding: var(--mj-space-1) var(--mj-space-3);\n font-weight: 600;\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-secondary);\n flex: none;\n}\n\n/* \u2500\u2500 live console \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.wfr-stall {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 12px;\n margin: 0 12px 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 45%, var(--mj-border-default));\n border-left: 4px solid var(--mj-status-warning);\n border-radius: var(--mj-radius-md);\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n font-size: var(--mj-font-size-sm);\n}\n.wfr-stall-body {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n.wfr-stall i { color: var(--mj-status-warning); }\n.wfr-stall-copy {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n.wfr-stall-copy code {\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n font-size: var(--mj-font-size-xs);\n}\n.wfr-stall-reason { color: var(--mj-text-secondary); }\n.wfr-stall-actions { display: flex; flex-wrap: wrap; gap: 6px; }\n\n.wfr-replay {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 12px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n}\n.wfr-replay i { color: var(--mj-text-secondary); }\n.wfr-replay input[type='range'] { flex: 1; accent-color: var(--mj-brand-primary); }\n\n.wfr-engine {\n display: flex;\n gap: 8px;\n overflow-x: auto;\n padding: 6px 12px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n font-size: var(--mj-font-size-xs);\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n}\n.wfr-tick {\n flex: none;\n padding: 3px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface);\n white-space: nowrap;\n}\n.wfr-tick-held {\n border-color: color-mix(in srgb, var(--mj-status-warning, #d97706) 45%, var(--mj-border-default));\n background: color-mix(in srgb, var(--mj-status-warning, #d97706) 8%, var(--mj-bg-surface));\n}\n.wfr-tick-paused { color: var(--mj-brand-primary); border-color: var(--mj-border-strong); }\n\n.wfr-insp-card {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 7px 10px;\n margin: 0 0 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface);\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-primary);\n}\n.wfr-insp-card code {\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n font-size: var(--mj-font-size-xs);\n}\n.wfr-insp-label {\n font-size: var(--mj-font-size-xs);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n}\n.wfr-insp-card[data-verdict='held'],\n.wfr-insp-bad {\n border-color: color-mix(in srgb, var(--mj-status-warning, #d97706) 45%, var(--mj-border-default));\n background: color-mix(in srgb, var(--mj-status-warning, #d97706) 8%, var(--mj-bg-surface));\n}\n.wfr-insp-card[data-verdict='satisfied'] .wfr-insp-label { color: var(--mj-status-success); }\n.wfr-insp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 8px; }\n.wfr-insp-hint {\n margin: 0 0 8px;\n font-size: var(--mj-font-size-xs);\n color: var(--mj-text-muted);\n}\n\n.wfr-bp-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 6px 12px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n}\n.wfr-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: var(--mj-font-size-xs);\n cursor: pointer;\n}\n.wfr-chip-break { color: var(--mj-status-error); border-color: color-mix(in srgb, var(--mj-status-error) 40%, transparent); }\n.wfr-chip-break i { font-size: 7px; }\n.wfr-chip-paused {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n cursor: default;\n}\n.wfr-chip-forced {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 40%, transparent);\n cursor: default;\n}\n.wfr-chip-x {\n font-size: 14px;\n line-height: 1;\n padding: 0 2px;\n}\n.wfr-chip-x:hover { color: var(--mj-text-primary); }\n\n.wfr-bp {\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n margin: 0 0 8px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n}\n.wfr-bp:hover { color: var(--mj-status-error); }\n.wfr-bp-on,\n.wfr-bp-on:hover { color: var(--mj-status-error); }\n.wfr-bp i { font-size: 10px; }\n.wfr-bp:disabled { opacity: 0.45; cursor: default; }\n\n.wfr-editor-card {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n margin: 0 0 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface-card);\n}\n.wfr-editor-danger {\n border-color: color-mix(in srgb, var(--mj-status-error) 40%, var(--mj-border-default));\n}\n.wfr-json-edit {\n width: 100%;\n min-height: 120px;\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n font-size: var(--mj-font-size-xs);\n resize: vertical;\n}\n.wfr-editor-error { color: var(--mj-status-error); font-size: var(--mj-font-size-xs); }\n.wfr-insp-failure { border-color: var(--mj-status-error); color: var(--mj-status-error); }\n"] }]
1835
1835
  }], () => [{ type: i0.ChangeDetectorRef }], { onViewportResized: [{
1836
1836
  type: HostListener,
1837
1837
  args: ['window:resize']