@njeybe/nativ 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +17 -0
  3. package/dist/server/studio-ui/document.d.ts +3 -0
  4. package/dist/server/studio-ui/document.js +22 -0
  5. package/dist/server/studio-ui/pages/benchmarks/index.d.ts +2 -0
  6. package/dist/server/studio-ui/pages/benchmarks/index.js +11 -0
  7. package/dist/server/studio-ui/pages/benchmarks/script.d.ts +1 -0
  8. package/dist/server/studio-ui/pages/benchmarks/script.js +104 -0
  9. package/dist/server/studio-ui/pages/benchmarks/styles.d.ts +1 -0
  10. package/dist/server/studio-ui/pages/benchmarks/styles.js +30 -0
  11. package/dist/server/studio-ui/pages/database/browser.d.ts +1 -0
  12. package/dist/server/studio-ui/pages/database/browser.js +326 -0
  13. package/dist/server/studio-ui/pages/database/connection.d.ts +1 -0
  14. package/dist/server/studio-ui/pages/database/connection.js +158 -0
  15. package/dist/server/studio-ui/pages/database/data-shell.d.ts +1 -0
  16. package/dist/server/studio-ui/pages/database/data-shell.js +165 -0
  17. package/dist/server/studio-ui/pages/database/drift.d.ts +1 -0
  18. package/dist/server/studio-ui/pages/database/drift.js +301 -0
  19. package/dist/server/studio-ui/pages/database/events.d.ts +1 -0
  20. package/dist/server/studio-ui/pages/database/events.js +231 -0
  21. package/dist/server/studio-ui/pages/database/explorer.d.ts +1 -0
  22. package/dist/server/studio-ui/pages/database/explorer.js +183 -0
  23. package/dist/server/studio-ui/pages/database/index.d.ts +2 -0
  24. package/dist/server/studio-ui/pages/database/index.js +28 -0
  25. package/dist/server/studio-ui/pages/database/markup.d.ts +1 -0
  26. package/dist/server/studio-ui/pages/database/markup.js +32 -0
  27. package/dist/server/studio-ui/pages/database/state.d.ts +2 -0
  28. package/dist/server/studio-ui/pages/database/state.js +57 -0
  29. package/dist/server/studio-ui/pages/database/styles.d.ts +1 -0
  30. package/dist/server/studio-ui/pages/database/styles.js +118 -0
  31. package/dist/server/studio-ui/pages/flow/index.d.ts +2 -0
  32. package/dist/server/studio-ui/pages/flow/index.js +11 -0
  33. package/dist/server/studio-ui/pages/flow/layout.d.ts +1 -0
  34. package/dist/server/studio-ui/pages/flow/layout.js +105 -0
  35. package/dist/server/studio-ui/pages/flow/plan-view.d.ts +1 -0
  36. package/dist/server/studio-ui/pages/flow/plan-view.js +67 -0
  37. package/dist/server/studio-ui/pages/flow/script.d.ts +1 -0
  38. package/dist/server/studio-ui/pages/flow/script.js +61 -0
  39. package/dist/server/studio-ui/pages/flow/styles.d.ts +1 -0
  40. package/dist/server/studio-ui/pages/flow/styles.js +59 -0
  41. package/dist/server/studio-ui/pages/flow/timeline-model.d.ts +1 -0
  42. package/dist/server/studio-ui/pages/flow/timeline-model.js +95 -0
  43. package/dist/server/studio-ui/pages/flow/timeline-view.d.ts +1 -0
  44. package/dist/server/studio-ui/pages/flow/timeline-view.js +92 -0
  45. package/dist/server/studio-ui/pages/home/index.d.ts +2 -0
  46. package/dist/server/studio-ui/pages/home/index.js +11 -0
  47. package/dist/server/studio-ui/pages/home/script.d.ts +1 -0
  48. package/dist/server/studio-ui/pages/home/script.js +160 -0
  49. package/dist/server/studio-ui/pages/home/styles.d.ts +1 -0
  50. package/dist/server/studio-ui/pages/home/styles.js +48 -0
  51. package/dist/server/studio-ui/pages/index.d.ts +2 -0
  52. package/dist/server/studio-ui/pages/index.js +17 -0
  53. package/dist/server/studio-ui/pages/tasks/index.d.ts +2 -0
  54. package/dist/server/studio-ui/pages/tasks/index.js +11 -0
  55. package/dist/server/studio-ui/pages/tasks/script.d.ts +1 -0
  56. package/dist/server/studio-ui/pages/tasks/script.js +96 -0
  57. package/dist/server/studio-ui/pages/tasks/styles.d.ts +1 -0
  58. package/dist/server/studio-ui/pages/tasks/styles.js +37 -0
  59. package/dist/server/studio-ui/pages/team/index.d.ts +2 -0
  60. package/dist/server/studio-ui/pages/team/index.js +11 -0
  61. package/dist/server/studio-ui/pages/team/script.d.ts +1 -0
  62. package/dist/server/studio-ui/pages/team/script.js +112 -0
  63. package/dist/server/studio-ui/pages/team/styles.d.ts +1 -0
  64. package/dist/server/studio-ui/pages/team/styles.js +22 -0
  65. package/dist/server/studio-ui/pages/types.d.ts +13 -0
  66. package/dist/server/studio-ui/pages/types.js +1 -0
  67. package/dist/server/studio-ui/pages/worktrees/index.d.ts +2 -0
  68. package/dist/server/studio-ui/pages/worktrees/index.js +11 -0
  69. package/dist/server/studio-ui/pages/worktrees/script.d.ts +1 -0
  70. package/dist/server/studio-ui/pages/worktrees/script.js +104 -0
  71. package/dist/server/studio-ui/pages/worktrees/styles.d.ts +1 -0
  72. package/dist/server/studio-ui/pages/worktrees/styles.js +15 -0
  73. package/dist/server/studio-ui/shell/actions.d.ts +1 -0
  74. package/dist/server/studio-ui/shell/actions.js +49 -0
  75. package/dist/server/studio-ui/shell/boot.d.ts +2 -0
  76. package/dist/server/studio-ui/shell/boot.js +13 -0
  77. package/dist/server/studio-ui/shell/console.d.ts +1 -0
  78. package/dist/server/studio-ui/shell/console.js +194 -0
  79. package/dist/server/studio-ui/shell/core.d.ts +1 -0
  80. package/dist/server/studio-ui/shell/core.js +50 -0
  81. package/dist/server/studio-ui/shell/detail-view.d.ts +1 -0
  82. package/dist/server/studio-ui/shell/detail-view.js +80 -0
  83. package/dist/server/studio-ui/shell/detail.d.ts +1 -0
  84. package/dist/server/studio-ui/shell/detail.js +83 -0
  85. package/dist/server/studio-ui/shell/dispatch.d.ts +1 -0
  86. package/dist/server/studio-ui/shell/dispatch.js +156 -0
  87. package/dist/server/studio-ui/shell/events-tools.d.ts +1 -0
  88. package/dist/server/studio-ui/shell/events-tools.js +94 -0
  89. package/dist/server/studio-ui/shell/format.d.ts +1 -0
  90. package/dist/server/studio-ui/shell/format.js +151 -0
  91. package/dist/server/studio-ui/shell/icons.d.ts +2 -0
  92. package/dist/server/studio-ui/shell/icons.js +20 -0
  93. package/dist/server/studio-ui/shell/markup-head.d.ts +1 -0
  94. package/dist/server/studio-ui/shell/markup-head.js +43 -0
  95. package/dist/server/studio-ui/shell/markup-panels.d.ts +2 -0
  96. package/dist/server/studio-ui/shell/markup-panels.js +7 -0
  97. package/dist/server/studio-ui/shell/markup-tools.d.ts +1 -0
  98. package/dist/server/studio-ui/shell/markup-tools.js +222 -0
  99. package/dist/server/studio-ui/shell/model.d.ts +1 -0
  100. package/dist/server/studio-ui/shell/model.js +192 -0
  101. package/dist/server/studio-ui/shell/paint.d.ts +1 -0
  102. package/dist/server/studio-ui/shell/paint.js +154 -0
  103. package/dist/server/studio-ui/shell/palette-view.d.ts +1 -0
  104. package/dist/server/studio-ui/shell/palette-view.js +60 -0
  105. package/dist/server/studio-ui/shell/palette.d.ts +1 -0
  106. package/dist/server/studio-ui/shell/palette.js +80 -0
  107. package/dist/server/studio-ui/shell/pipeline-state.d.ts +1 -0
  108. package/dist/server/studio-ui/shell/pipeline-state.js +152 -0
  109. package/dist/server/studio-ui/shell/proposals.d.ts +1 -0
  110. package/dist/server/studio-ui/shell/proposals.js +197 -0
  111. package/dist/server/studio-ui/shell/pure.d.ts +3 -0
  112. package/dist/server/studio-ui/shell/pure.js +9 -0
  113. package/dist/server/studio-ui/shell/router.d.ts +1 -0
  114. package/dist/server/studio-ui/shell/router.js +95 -0
  115. package/dist/server/studio-ui/shell/sidebar-view.d.ts +1 -0
  116. package/dist/server/studio-ui/shell/sidebar-view.js +38 -0
  117. package/dist/server/studio-ui/shell/sync.d.ts +1 -0
  118. package/dist/server/studio-ui/shell/sync.js +120 -0
  119. package/dist/server/studio-ui/shell/toasts-format.d.ts +1 -0
  120. package/dist/server/studio-ui/shell/toasts-format.js +89 -0
  121. package/dist/server/studio-ui/shell/triage.d.ts +1 -0
  122. package/dist/server/studio-ui/shell/triage.js +210 -0
  123. package/dist/server/studio-ui/styles/console.d.ts +1 -0
  124. package/dist/server/studio-ui/styles/console.js +83 -0
  125. package/dist/server/studio-ui/styles/dialogs.d.ts +1 -0
  126. package/dist/server/studio-ui/styles/dialogs.js +118 -0
  127. package/dist/server/studio-ui/styles/responsive.d.ts +1 -0
  128. package/dist/server/studio-ui/styles/responsive.js +28 -0
  129. package/dist/server/studio-ui/styles/shell.d.ts +1 -0
  130. package/dist/server/studio-ui/styles/shell.js +117 -0
  131. package/dist/server/studio-ui/styles/strategist.d.ts +1 -0
  132. package/dist/server/studio-ui/styles/strategist.js +77 -0
  133. package/dist/server/studio-ui/styles/tokens-base.d.ts +1 -0
  134. package/dist/server/studio-ui/styles/tokens-base.js +131 -0
  135. package/dist/server/studio-ui.d.ts +1 -10
  136. package/dist/server/studio-ui.js +63 -5314
  137. package/package.json +2 -2
  138. package/plugin/.claude-plugin/plugin.json +1 -1
  139. package/plugin/agents/architect.md +9 -5
  140. package/templates/GEMINI.md +12 -8
  141. package/templates/claude-agents/architect.md +9 -5
  142. package/templates/dot-ai/subagents/flutter-developer.md +10 -0
  143. package/templates/dot-ai/subagents/frontend.md +10 -0
  144. package/templates/dot-ai/ui_specs.md +36 -6
@@ -0,0 +1,118 @@
1
+ // Database Studio view styles.
2
+ export const databaseCss = String.raw `/* Database Studio */
3
+ .telemetry { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
4
+ .pill { display: inline-flex; align-items: center; gap: 8px; max-width: 100%; padding: 4px 10px; font-size: 13px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-control); box-shadow: var(--shadow); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
5
+ .pill b { font-weight: 600; }
6
+ .diagnostic-banner { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 16px; margin: 12px 0 16px; background: var(--danger-bg); border: 1px solid var(--danger-line); border-radius: var(--r-control); }
7
+ .diagnostic-content { display: flex; align-items: center; gap: 10px; font-size: 13.5px; }
8
+ .diagnostic-tag { font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--danger); background: var(--surface); border: 1px solid var(--danger-line); padding: 0 7px; border-radius: var(--r-control); }
9
+ .diagnostic-msg code { background: var(--surface); padding: 1px 6px; border-radius: 4px; font-family: var(--mono); }
10
+ .diagnostic-actions { display: flex; gap: 8px; }
11
+ .template-helper-box { background: var(--active-bg); border: 1px solid var(--active-line); border-radius: var(--r-control); padding: 12px 14px; margin-bottom: 16px; }
12
+ .template-helper-label { font-size: 12.5px; color: var(--muted); margin-bottom: 8px; }
13
+ .template-helper-label code { color: var(--active); font-weight: 600; }
14
+ .template-chips { display: flex; flex-wrap: wrap; gap: 8px; }
15
+ .template-chip { font-family: var(--mono); font-size: 12.5px; }
16
+ .template-chip:hover { border-color: var(--active); }
17
+ .chip-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
18
+ .chip-dot.on { background: var(--success); }
19
+ .chip-dot.off { background: var(--danger); }
20
+
21
+ .thumb-pop { position: fixed; z-index: 60; width: 220px; padding: 8px; border-radius: var(--r-card); border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow-float); pointer-events: none; }
22
+ .thumb-box { display: grid; place-items: center; min-height: 120px; max-height: 200px; overflow: hidden; border-radius: var(--r-control); background: repeating-conic-gradient(var(--chip) 0% 25%, var(--surface) 0% 50%) 50% / 16px 16px; }
23
+ .thumb-box img { max-width: 100%; max-height: 200px; display: block; }
24
+ .thumb-box .ph { color: var(--muted); font-size: 12px; text-align: center; padding: 12px; }
25
+ .thumb-meta { margin-top: 6px; font-size: 11.5px; color: var(--muted); overflow-wrap: anywhere; }
26
+ .media-modal .thumb-box { min-height: 220px; max-height: 360px; }
27
+ .media-modal .thumb-box img { max-height: 360px; }
28
+ .media-modal dl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; margin: 14px 0 0; font-size: 13px; }
29
+ .media-modal dt { color: var(--muted); }
30
+ .media-modal dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
31
+ .media-note { margin-top: 12px; font-size: 13px; line-height: 1.5; color: var(--muted); }
32
+
33
+ .toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 14px; }
34
+ .input { min-height: 32px; padding: 6px 10px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-control); min-width: 0; }
35
+ .input:focus { border-color: var(--primary); outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent); }
36
+ textarea.input { width: 100%; resize: vertical; line-height: 1.5; }
37
+ .search { flex: 1; min-width: 180px; max-width: 420px; }
38
+ .panes { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
39
+ .panes-full { grid-template-columns: 1fr; }
40
+ .pane { padding: 16px; min-width: 0; }
41
+ .pane-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 12px; }
42
+ .pane-head h2 { margin: 0; font-size: 15.5px; display: flex; align-items: center; gap: 8px; white-space: nowrap; flex: none; }
43
+ .pane-head .meta { color: var(--muted); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
44
+ .table-card { border: 1px solid var(--border); border-radius: var(--r-control); margin-bottom: 8px; background: var(--surface); }
45
+ .table-card > summary { list-style: none; cursor: pointer; padding: 10px 12px; display: flex; align-items: center; gap: 8px; border-radius: var(--r-control); }
46
+ .table-card > summary::-webkit-details-marker { display: none; }
47
+ .table-card > summary:hover { background: var(--bg); }
48
+ .table-card > summary::before { content: "\25B8"; color: var(--muted); transition: transform .15s; }
49
+ .table-card[open] > summary::before { transform: rotate(90deg); }
50
+ .table-card .tname { font-family: var(--mono); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
51
+ .table-card .ccount { margin-left: auto; color: var(--muted); font-size: 12.5px; white-space: nowrap; }
52
+ .table-body { padding: 0 12px 12px; overflow-x: auto; }
53
+ .desc { color: var(--muted); margin: 0 0 8px; font-size: 13.5px; line-height: 1.5; }
54
+ table.cols { width: 100%; border-collapse: collapse; font-size: 13px; }
55
+ table.cols th { text-align: left; color: var(--muted); font-weight: 500; font-size: 12.5px; padding: 6px 8px; border-bottom: 1px solid var(--border); white-space: nowrap; }
56
+ table.cols td { padding: 6px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
57
+ table.cols td.mono { white-space: nowrap; }
58
+ .subhead { font-size: 12.5px; color: var(--muted); margin: 12px 0 6px; text-transform: uppercase; letter-spacing: .05em; }
59
+ .idx-list { margin: 0; padding-left: 18px; font-size: 13px; }
60
+
61
+ .summary-banner { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; padding: 16px; margin-bottom: 16px; }
62
+ .stat { padding: 4px 8px; }
63
+ .stat .n { font-size: 26px; font-weight: 650; line-height: 1.1; font-variant-numeric: tabular-nums; }
64
+ .stat .l { color: var(--muted); font-size: 12.5px; }
65
+ .risk-HIGH { color: var(--danger); } .risk-MEDIUM { color: var(--active); } .risk-LOW { color: var(--success); } .risk-NONE { color: var(--muted); }
66
+ .alert-danger { border: 1px solid var(--danger-line); background: var(--danger-bg); border-radius: var(--r-control); padding: 10px 12px; margin-bottom: 12px; }
67
+ .alert-danger strong { color: var(--danger); }
68
+ .diff-table td.before { color: var(--danger); } .diff-table td.after { color: var(--success); }
69
+ .row-added td { background: var(--success-bg); } .row-dropped td { background: var(--danger-bg); } .row-altered td { background: var(--active-bg); }
70
+ .chip-list { display: flex; flex-wrap: wrap; gap: 6px; }
71
+ .chip { font-family: var(--mono); font-size: 12px; padding: 1px 8px; border-radius: var(--r-control); border: 1px solid var(--chip-border); background: var(--chip); }
72
+
73
+ .sql-wrap { position: relative; padding: 0; overflow: hidden; }
74
+ .sql-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--border); }
75
+ pre.sql { margin: 0; padding: 16px; overflow: auto; max-height: 70vh; background: var(--bg); font-family: var(--mono); font-size: 12.5px; line-height: 1.6; white-space: pre; }
76
+ .sql .k { color: var(--primary); font-weight: 600; } .sql .s { color: var(--success); } .sql .c { color: var(--muted); font-style: italic; } .sql .d { color: var(--danger); font-weight: 700; }
77
+ .sql .p { color: var(--active); } .sql .n { color: var(--active); font-weight: 600; } .sql .o { color: var(--primary); }
78
+ .lang { font-family: var(--mono); font-size: 11px; padding: 0 7px; border-radius: var(--r-control); border: 1px solid var(--primary-line); background: var(--primary-tint); color: var(--primary); margin-right: 6px; }
79
+ .copy-wrap { position: relative; display: inline-flex; }
80
+ .tooltip { position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%); background: var(--success); color: #fff; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: var(--r-control); pointer-events: none; opacity: 0; transition: opacity .15s; white-space: nowrap; }
81
+ .tooltip.show { opacity: 1; }
82
+
83
+ .empty { text-align: center; padding: 48px 24px; }
84
+ .empty .empty-icon { color: var(--primary); margin: 0 auto 12px; }
85
+ .empty h3 { margin: 0 0 6px; font-size: 16px; }
86
+ .empty p { color: var(--muted); margin: 0 0 16px; font-size: 15px; line-height: 1.55; }
87
+
88
+ .env-toggle-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin: 14px 0 6px; }
89
+ .env-segmented { display: inline-flex; gap: 2px; padding: 3px; background: var(--chip); border: 1px solid var(--border); border-radius: var(--r-control); }
90
+ .env-seg-btn { border: 1px solid transparent; background: transparent; padding: 4px 14px; border-radius: 5px; cursor: pointer; color: var(--muted); font-size: 13px; font-weight: 600; transition: all .15s ease; }
91
+ .env-seg-btn:hover { color: var(--text); }
92
+ .env-seg-btn[aria-pressed="true"] { background: var(--surface); color: var(--primary); border-color: var(--border); box-shadow: var(--shadow); }
93
+ .env-seg-btn[data-env-mode="prod"][aria-pressed="true"] { color: var(--danger); background: var(--danger-bg); border-color: var(--danger-line); }
94
+ .prod-banner { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 16px; border-radius: var(--r-control); border: 1px solid var(--danger-line); background: var(--danger-bg); font-size: 13px; margin-bottom: 14px; }
95
+ .prod-banner b { color: var(--danger); font-weight: 600; }
96
+
97
+ .tabs { display: flex; gap: 4px; margin: 16px 0; border-bottom: 1px solid var(--border); overflow-x: auto; overflow-y: hidden; }
98
+ .subtab { background: none; border: none; padding: 10px 12px; cursor: pointer; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 500; }
99
+ .subtab:hover { color: var(--text); }
100
+ .subtab[aria-selected="true"] { color: var(--text); border-bottom-color: var(--primary); }
101
+
102
+ .data-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
103
+ .data-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; flex: 1; min-width: 0; }
104
+ .data-grid-wrap { overflow-x: auto; max-width: 100%; border: 1px solid var(--border); border-radius: var(--r-card); background: var(--surface); box-shadow: var(--shadow); margin-bottom: 14px; min-height: 240px; }
105
+ table.data-table { width: 100%; border-collapse: collapse; font-size: 13px; text-align: left; }
106
+ table.data-table th { background: var(--bg); color: var(--muted); font-weight: 600; font-size: 12.5px; padding: 10px 12px; border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 2; white-space: nowrap; user-select: none; }
107
+ table.data-table th.sortable { cursor: pointer; }
108
+ table.data-table th.sortable:hover { color: var(--text); background: var(--chip); }
109
+ table.data-table td { padding: 8px 12px; border-bottom: 1px solid var(--border); max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; }
110
+ table.data-table tr:hover td { background: var(--bg); }
111
+ .th-sort-icon { font-size: 10px; margin-left: 4px; color: var(--primary); }
112
+ .td-actions { display: flex; gap: 6px; align-items: center; white-space: nowrap; }
113
+ .pagination-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 4px; font-size: 13px; color: var(--muted); }
114
+ .pagination-ctrls { display: flex; align-items: center; gap: 8px; }
115
+ .cell-preview-btn { padding: 1px 6px; font-size: 11px; border-radius: 4px; border: 1px solid var(--primary-line); color: var(--primary); background: var(--primary-tint); cursor: pointer; }
116
+ .cell-preview-btn:hover { background: var(--surface); }
117
+
118
+ `;
@@ -0,0 +1,2 @@
1
+ import type { PageModule } from '../types.js';
2
+ export declare const flowPage: PageModule;
@@ -0,0 +1,11 @@
1
+ import { ICON_PATHS } from '../../shell/icons.js';
2
+ import { flowScript } from './script.js';
3
+ import { flowCss } from './styles.js';
4
+ export const flowPage = {
5
+ id: 'flow',
6
+ group: 'now',
7
+ label: 'Flow',
8
+ icon: ICON_PATHS.flow,
9
+ css: flowCss,
10
+ script: flowScript,
11
+ };
@@ -0,0 +1 @@
1
+ export declare const flowLayoutScript: string;
@@ -0,0 +1,105 @@
1
+ // Plan layout: rounds, lanes, links and the highlight set. Pure, no DOM.
2
+ export const flowLayoutScript = String.raw ` var FLOW = { W: 206, H: 58, GX: 52, GY: 10, PAD: 14, LX: 128, TOP: 30 };
3
+
4
+ function flowAgentKey(t) { return t.agent || 'unassigned'; }
5
+
6
+ function flowGraph(m) {
7
+ var ids = {};
8
+ m.tasks.forEach(function (t) { ids[t.id] = t; });
9
+ var depsIn = function (id) {
10
+ return ids[id].deps.filter(function (d) { return ids[d] && d !== id; });
11
+ };
12
+ var depth = {}, anc = {};
13
+ function getD(id, seen) {
14
+ if (depth[id] != null) return depth[id];
15
+ if (seen[id]) return 0;
16
+ seen[id] = true;
17
+ var d = 0;
18
+ depsIn(id).forEach(function (p) { d = Math.max(d, 1 + getD(p, seen)); });
19
+ delete seen[id];
20
+ depth[id] = d;
21
+ return d;
22
+ }
23
+ function getA(id, seen) {
24
+ if (anc[id]) return anc[id];
25
+ if (seen[id]) return {};
26
+ seen[id] = true;
27
+ var out = {};
28
+ depsIn(id).forEach(function (p) {
29
+ out[p] = true;
30
+ var up = getA(p, seen);
31
+ Object.keys(up).forEach(function (k) { out[k] = true; });
32
+ });
33
+ delete seen[id];
34
+ anc[id] = out;
35
+ return out;
36
+ }
37
+ m.tasks.forEach(function (t) { getD(t.id, {}); getA(t.id, {}); });
38
+ var edges = [], kids = {};
39
+ m.tasks.forEach(function (t) {
40
+ depsIn(t.id).forEach(function (d) {
41
+ var implied = depsIn(t.id).some(function (e) { return e !== d && getA(e, {})[d]; });
42
+ edges.push({ from: d, to: t.id, implied: implied });
43
+ (kids[d] = kids[d] || []).push(t.id);
44
+ });
45
+ });
46
+ return { depth: depth, anc: function (id) { return getA(id, {}); }, edges: edges, kids: kids };
47
+ }
48
+
49
+ function flowPlace(m, g) {
50
+ var lanes = [], cells = {};
51
+ m.tasks.forEach(function (t) {
52
+ var a = flowAgentKey(t);
53
+ if (lanes.indexOf(a) === -1) lanes.push(a);
54
+ var k = a + '|' + g.depth[t.id];
55
+ (cells[k] = cells[k] || []).push(t);
56
+ });
57
+ var rounds = Math.max.apply(null, m.tasks.map(function (t) { return g.depth[t.id]; })) + 1;
58
+ var laneY = {}, laneH = {}, y = FLOW.TOP;
59
+ lanes.forEach(function (l) {
60
+ var stack = 1;
61
+ for (var r = 0; r < rounds; r++) stack = Math.max(stack, (cells[l + '|' + r] || []).length);
62
+ laneY[l] = y;
63
+ laneH[l] = FLOW.PAD * 2 + stack * FLOW.H + (stack - 1) * FLOW.GY;
64
+ y += laneH[l];
65
+ });
66
+ var pos = {};
67
+ Object.keys(cells).forEach(function (k) {
68
+ var bits = k.split('|'), r = Number(bits[1]);
69
+ cells[k].forEach(function (t, i) {
70
+ pos[t.id] = { x: FLOW.LX + 20 + r * (FLOW.W + FLOW.GX), y: laneY[bits[0]] + FLOW.PAD + i * (FLOW.H + FLOW.GY), round: r + 1 };
71
+ });
72
+ });
73
+ return { lanes: lanes, laneY: laneY, laneH: laneH, pos: pos, rounds: rounds,
74
+ width: FLOW.LX + 20 + rounds * (FLOW.W + FLOW.GX), height: y };
75
+ }
76
+
77
+ /** Everything the Plan view draws for one milestone. */
78
+ function flowLayout(m) {
79
+ var g = flowGraph(m);
80
+ var L = flowPlace(m, g);
81
+ L.edges = g.edges;
82
+ L.kids = g.kids;
83
+ L.anc = g.anc;
84
+ L.hidden = g.edges.filter(function (e) { return e.implied; }).length;
85
+ return L;
86
+ }
87
+
88
+ /** The task, everything it needs and everything it unblocks, or null when it is not drawn. */
89
+ function flowFocusSet(L, id) {
90
+ if (!id || !L.pos[id]) return null;
91
+ var hot = {}, down = {}, stack = [id];
92
+ hot[id] = true;
93
+ Object.keys(L.anc(id)).forEach(function (k) { hot[k] = true; });
94
+ while (stack.length) {
95
+ (L.kids[stack.pop()] || []).forEach(function (c) {
96
+ if (down[c]) return;
97
+ down[c] = true;
98
+ hot[c] = true;
99
+ stack.push(c);
100
+ });
101
+ }
102
+ return hot;
103
+ }
104
+
105
+ `;
@@ -0,0 +1 @@
1
+ export declare const flowPlanScript: string;
@@ -0,0 +1,67 @@
1
+ // Plan view: swimlanes per agent, one column per round, bezier links inside the milestone.
2
+ export const flowPlanScript = String.raw ` function flowWires(L, hot, showAll) {
3
+ return L.edges.filter(function (e) { return showAll || !e.implied; }).map(function (e) {
4
+ var a = L.pos[e.from], b = L.pos[e.to];
5
+ var x1 = a.x + FLOW.W, y1 = a.y + FLOW.H / 2, x2 = b.x, y2 = b.y + FLOW.H / 2;
6
+ var dx = Math.max(28, (x2 - x1) / 2);
7
+ var on = hot && hot[e.from] && hot[e.to];
8
+ return '<path class="wire' + (on ? ' hot' : '') + (e.implied ? ' implied' : '') + '" data-from="' + ui.esc(e.from) +
9
+ '" data-to="' + ui.esc(e.to) + '" d="M' + x1 + ' ' + y1 + ' C' + (x1 + dx) + ' ' + y1 + ', ' + (x2 - dx) + ' ' + y2 + ', ' + x2 + ' ' + y2 + '"/>';
10
+ }).join('');
11
+ }
12
+
13
+ function flowBands(m, L) {
14
+ return L.lanes.map(function (l) {
15
+ var count = m.tasks.filter(function (t) { return flowAgentKey(t) === l; }).length;
16
+ return '<div class="lane-band" data-lane="' + ui.esc(l) + '" style="top:' + L.laneY[l] + 'px;height:' + L.laneH[l] + 'px">' +
17
+ '<div class="lane-label">' + ui.agentChip(l === 'unassigned' ? null : l) + '<span class="faint">' + count + (count === 1 ? ' task' : ' tasks') + '</span></div></div>';
18
+ }).join('');
19
+ }
20
+
21
+ function flowRoundHeads(L) {
22
+ var out = '';
23
+ for (var r = 0; r < L.rounds; r++) {
24
+ out += '<div class="round-head" style="left:' + (FLOW.LX + 20 + r * (FLOW.W + FLOW.GX)) + 'px">Round ' + (r + 1) + '</div>';
25
+ }
26
+ return out;
27
+ }
28
+
29
+ function flowNodeTime(t) {
30
+ if (t.status === 'in_progress' && t.startT) return '<span data-since="' + t.startT + '">' + ui.esc(fmt.dur(Studio.now() - t.startT)) + '</span>';
31
+ return t.durationMs ? ui.esc(fmt.dur(t.durationMs)) : '';
32
+ }
33
+
34
+ function flowNodes(m, L, hot, focusId) {
35
+ return m.tasks.map(function (t) {
36
+ var p = L.pos[t.id];
37
+ var cls = 'node ' + t.status + (hot && hot[t.id] ? ' hot' : '') + (focusId === t.id ? ' sel' : '');
38
+ return '<button type="button" class="' + ui.esc(cls) + '" data-act="flow-focus" data-key="' + ui.esc(t.id) + '" data-id="' + ui.esc(t.id) +
39
+ '" data-round="' + p.round + '" aria-pressed="' + (focusId === t.id) + '" title="' + ui.esc(t.title) + '" style="left:' + p.x + 'px;top:' + p.y +
40
+ 'px;width:' + FLOW.W + 'px;height:' + FLOW.H + 'px"><div class="l1">' + ui.statusIcon(t.status) + '<span class="mono faint">' + ui.esc(t.id) +
41
+ '</span><span class="d">' + flowNodeTime(t) + '</span></div><div class="title">' + ui.esc(t.title) + '</div></button>';
42
+ }).join('');
43
+ }
44
+
45
+ function flowLegend() {
46
+ var one = function (s) { return '<span>' + ui.statusIcon(s) + Studio.statusLabel(s) + '</span>'; };
47
+ return '<div class="legend">' + ['completed', 'in_progress', 'pending', 'blocked'].map(one).join('') + '</div>';
48
+ }
49
+
50
+ function flowPlanTools(L, hot, view) {
51
+ var hint = hot ? '<button type="button" class="btn ghost" data-act="flow-clear">Clear highlight</button>'
52
+ : '<span>Click a task to light up what it needs and what it unblocks</span>';
53
+ var implied = L.hidden ? '<label class="check"><input type="checkbox" data-act="flow-implied"' + (view.implied ? ' checked' : '') +
54
+ '> Show ' + L.hidden + ' implied link' + (L.hidden > 1 ? 's' : '') + '</label>' : '';
55
+ return '<div class="fl-bar">' + flowLegend() + '<span class="right">' + hint + implied + '</span></div>';
56
+ }
57
+
58
+ function flowPlan(m, focusId, view) {
59
+ var L = flowLayout(m);
60
+ var hot = flowFocusSet(L, focusId);
61
+ return '<section class="fl-panel" data-mode="plan">' + flowPlanTools(L, hot, view) +
62
+ '<div class="flow-wrap"><div class="canvas' + (hot ? ' focus' : '') + '" style="width:' + L.width + 'px;height:' + (L.height + 10) + 'px">' +
63
+ flowBands(m, L) + flowRoundHeads(L) + '<svg class="wires" width="' + L.width + '" height="' + L.height + '">' +
64
+ flowWires(L, hot, view.implied) + '</svg>' + flowNodes(m, L, hot, hot ? focusId : null) + '</div></div></section>';
65
+ }
66
+
67
+ `;
@@ -0,0 +1 @@
1
+ export declare const flowScript: string;
@@ -0,0 +1,61 @@
1
+ import { flowLayoutScript } from './layout.js';
2
+ import { flowPlanScript } from './plan-view.js';
3
+ import { flowTimeModelScript } from './timeline-model.js';
4
+ import { flowTimelineScript } from './timeline-view.js';
5
+ // Flow page: registration, header and click handling. Views live in the sibling files.
6
+ const flowMainScript = String.raw ` var ui = Studio.ui, fmt = Studio.fmt;
7
+ var view = { mode: 'plan', implied: false };
8
+
9
+ function flowHead(m) {
10
+ var seg = function (mode, label) {
11
+ return '<button type="button" data-act="flow-mode" data-mode="' + mode + '" aria-pressed="' + (view.mode === mode) + '">' + label + '</button>';
12
+ };
13
+ return '<div class="panel-head"><div class="panel-title"><h1>Flow</h1><p class="lead">' +
14
+ (m ? ui.esc(m.name) : 'How the work in one milestone connects and how it actually ran.') + '</p></div>' +
15
+ '<div class="panel-actions">' + ui.milestoneSelect() + '<div class="seg" role="group" aria-label="Flow view">' +
16
+ seg('plan', 'Plan') + seg('timeline', 'What happened') + '</div></div></div>';
17
+ }
18
+
19
+ function flowShowFocus() {
20
+ var id = Studio.state.focusTaskId;
21
+ if (!id || typeof document === 'undefined') return;
22
+ setTimeout(function () {
23
+ var el = document.querySelector('#panel-flow [data-id="' + String(id).replace(/["\\]/g, '') + '"]');
24
+ if (el && el.scrollIntoView) el.scrollIntoView({ block: 'nearest', inline: 'center' });
25
+ }, 0);
26
+ }
27
+
28
+ Studio.registerPage('flow', {
29
+ deps: ['tasks', 'telemetry', 'runs'],
30
+ onShow: flowShowFocus,
31
+ render: function (ctx) {
32
+ var wanted = ctx.state.milestoneId || ctx.model.activeMilestoneId;
33
+ var m = ctx.model.milestones.filter(function (x) { return x.id === wanted; })[0];
34
+ if (!m) return flowHead(null) + ui.empty('No milestones yet', 'Add tasks to the plan to see them here.');
35
+ if (ctx.state.focusTaskId) view.mode = 'plan';
36
+ var body = !m.tasks.length ? ui.empty('This milestone has no tasks yet.', '')
37
+ : view.mode === 'plan' ? flowPlan(m, ctx.state.focusTaskId, view) : flowTimeline(m, ctx);
38
+ return flowHead(m) + body;
39
+ },
40
+ actions: {
41
+ 'flow-mode': function (el) {
42
+ view.mode = el.getAttribute('data-mode') === 'timeline' ? 'timeline' : 'plan';
43
+ Studio.state.focusTaskId = null;
44
+ Studio.repaint('flow');
45
+ },
46
+ 'flow-focus': function (el) {
47
+ var id = el.getAttribute('data-id');
48
+ if (Studio.state.focusTaskId === id) { Studio.closeDetail(); return; }
49
+ Studio.state.focusTaskId = id;
50
+ Studio.openTask(id);
51
+ Studio.repaint('flow');
52
+ },
53
+ 'flow-clear': function () { Studio.closeDetail(); },
54
+ 'flow-implied': function (el) {
55
+ view.implied = !!el.checked;
56
+ Studio.repaint('flow');
57
+ }
58
+ }
59
+ });
60
+ `;
61
+ export const flowScript = [flowLayoutScript, flowPlanScript, flowTimeModelScript, flowTimelineScript, flowMainScript].join('');
@@ -0,0 +1 @@
1
+ export declare const flowCss: string;
@@ -0,0 +1,59 @@
1
+ // Flow page styles, all scoped under #panel-flow.
2
+ export const flowCss = String.raw `#panel-flow .seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--surface); }
3
+ #panel-flow .seg button { border: 0; background: none; padding: 0 12px; height: 30px; font-size: 13px; color: var(--fg-2); cursor: pointer; }
4
+ #panel-flow .seg button + button { border-left: 1px solid var(--line); }
5
+ #panel-flow .seg button[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); font-weight: 500; }
6
+ #panel-flow .fl-panel { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; min-width: 0; overflow: hidden; }
7
+ #panel-flow .fl-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 14px; padding: 12px 16px; }
8
+ #panel-flow .fl-bar .right { margin-left: auto; display: flex; gap: 14px; align-items: center; flex-wrap: wrap; font-size: 12.5px; color: var(--fg-3); }
9
+ #panel-flow .flow-wrap { overflow: auto; max-height: 72vh; position: relative; border-top: 1px solid var(--line); }
10
+ #panel-flow .canvas { position: relative; }
11
+ #panel-flow .canvas svg.wires { position: absolute; inset: 0; pointer-events: none; overflow: visible; }
12
+ #panel-flow .wire { fill: none; stroke: var(--line-2); stroke-width: 1.5; transition: opacity .15s, stroke .15s; }
13
+ #panel-flow .wire.implied { stroke-dasharray: 4 4; }
14
+ #panel-flow .wire.hot { stroke: var(--accent); stroke-width: 2; }
15
+ #panel-flow .canvas.focus .wire:not(.hot) { opacity: .15; }
16
+ #panel-flow .lane-band { position: absolute; left: 0; right: 0; border-top: 1px solid var(--line); }
17
+ #panel-flow .lane-band:nth-child(even) { background: color-mix(in srgb, var(--surface-2) 45%, transparent); }
18
+ #panel-flow .lane-label { position: sticky; left: 0; width: 128px; padding: 10px 12px; display: flex; flex-direction: column; gap: 2px; background: var(--surface); border-right: 1px solid var(--line); height: 100%; z-index: 2; }
19
+ #panel-flow .lane-label .faint { font-size: 11.5px; }
20
+ #panel-flow .round-head { position: absolute; top: 0; height: 30px; display: flex; align-items: center; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-3); }
21
+ #panel-flow .node { position: absolute; display: flex; flex-direction: column; justify-content: center; gap: 3px; padding: 7px 10px; background: var(--surface); border: 1px solid var(--line); border-radius: 9px; text-align: left; cursor: pointer; transition: opacity .15s, border-color .15s, box-shadow .15s; z-index: 1; }
22
+ #panel-flow .node:hover { border-color: var(--line-2); box-shadow: var(--shadow); }
23
+ #panel-flow .node.in_progress { border-color: color-mix(in srgb, var(--run) 50%, var(--line)); background: color-mix(in srgb, var(--run-soft) 55%, var(--surface)); }
24
+ #panel-flow .node.blocked { border-color: color-mix(in srgb, var(--bad) 50%, var(--line)); background: color-mix(in srgb, var(--bad-soft) 55%, var(--surface)); }
25
+ #panel-flow .node .l1 { display: flex; align-items: center; gap: 6px; }
26
+ #panel-flow .node .l1 .d { margin-left: auto; font: 11px var(--mono); color: var(--fg-3); }
27
+ #panel-flow .node .l1 .mono { font-size: 11px; }
28
+ #panel-flow .node .title { font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
29
+ #panel-flow .canvas.focus .node:not(.hot) { opacity: .3; }
30
+ #panel-flow .node.sel { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
31
+ #panel-flow .legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--fg-2); align-items: center; }
32
+ #panel-flow .legend span { display: inline-flex; align-items: center; gap: 6px; }
33
+ #panel-flow .check { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--fg-2); }
34
+ #panel-flow .stats { display: flex; flex-wrap: wrap; }
35
+ #panel-flow .stats > div { padding: 12px 16px; display: flex; flex-direction: column; gap: 1px; min-width: 140px; }
36
+ #panel-flow .stats > div + div { border-left: 1px solid var(--line); }
37
+ #panel-flow .stats .k { font-size: 12px; color: var(--fg-3); }
38
+ #panel-flow .stats .v { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
39
+ #panel-flow .stats .s { font-size: 12px; color: var(--fg-2); }
40
+ #panel-flow svg.tl { display: block; }
41
+ #panel-flow svg.tl text { font-family: var(--font); font-size: 11px; fill: var(--fg-3); }
42
+ #panel-flow svg.tl .lane-t { fill: var(--fg); font-weight: 500; font-size: 12px; }
43
+ #panel-flow svg.tl .grid { stroke: var(--line); stroke-width: 1; }
44
+ #panel-flow svg.tl .band { fill: var(--surface-2); opacity: .5; }
45
+ #panel-flow svg.tl .gap { fill: var(--surface-2); }
46
+ #panel-flow svg.tl .gap-line { stroke: var(--line-2); stroke-dasharray: 3 3; }
47
+ #panel-flow svg.tl .bar-w { fill: color-mix(in srgb, var(--ok) 30%, var(--surface)); stroke: var(--ok); stroke-width: 1; cursor: pointer; }
48
+ #panel-flow svg.tl .bar-c { fill: var(--ok); pointer-events: none; }
49
+ #panel-flow svg.tl .bar-w.in_progress { fill: url(#fl-stripes); stroke: var(--run); }
50
+ #panel-flow svg.tl .bar-w.blocked { fill: color-mix(in srgb, var(--bad) 22%, var(--surface)); stroke: var(--bad); }
51
+ #panel-flow svg.tl .bar-w:hover { stroke-width: 2; }
52
+ #panel-flow svg.tl .bar-l { fill: var(--fg); font-size: 11px; pointer-events: none; font-family: var(--mono); }
53
+ #panel-flow svg.tl .now-line { stroke: var(--run); stroke-width: 1.5; }
54
+ #panel-flow svg.tl .stripe-a { fill: var(--run-soft); }
55
+ #panel-flow svg.tl .stripe-b { fill: color-mix(in srgb, var(--run) 30%, var(--surface)); }
56
+ #panel-flow .fl-untimed { padding: 12px 16px; border-top: 1px solid var(--line); font-size: 12.5px; color: var(--fg-3); }
57
+ #panel-flow .fl-untimed button { border: 0; background: none; padding: 0; color: var(--fg-2); cursor: pointer; }
58
+ #panel-flow .fl-untimed button:hover { text-decoration: underline; }
59
+ `;
@@ -0,0 +1 @@
1
+ export declare const flowTimeModelScript: string;
@@ -0,0 +1,95 @@
1
+ // Timeline numbers: bar times, squeezed idle gaps, agent rows and summary figures. Pure.
2
+ export const flowTimeModelScript = String.raw ` var TL = { GAP: 15 * 60000, MIN_BAR: 15000, LW: 130, GW: 40, ROWH: 30, TOPY: 30 };
3
+
4
+ function flowEnd(t, now) {
5
+ if (t.status === 'in_progress') return Math.max(now, t.startT + 1000);
6
+ if (t.endT && t.endT > t.startT) return t.endT;
7
+ return t.startT + (t.durationMs || TL.MIN_BAR);
8
+ }
9
+
10
+ /** Merges sorted [start, end] pairs; touching within slack counts as one stretch. */
11
+ function flowMerge(iv, slack) {
12
+ var out = [];
13
+ iv.forEach(function (p) {
14
+ var last = out[out.length - 1];
15
+ if (last && p[0] - last[1] <= slack) last[1] = Math.max(last[1], p[1]);
16
+ else out.push([p[0], p[1]]);
17
+ });
18
+ return out;
19
+ }
20
+
21
+ function flowSummary(timed, now) {
22
+ var iv = timed.map(function (t) { return [t.startT, flowEnd(t, now)]; }).sort(function (a, b) { return a[0] - b[0]; });
23
+ var busy = flowMerge(iv, 0);
24
+ var busyMs = busy.reduce(function (s, p) { return s + p[1] - p[0]; }, 0);
25
+ var last = Math.max.apply(null, iv.map(function (p) { return p[1]; }));
26
+ var span = last - iv[0][0];
27
+ var longest = 0, at = null;
28
+ for (var i = 1; i < busy.length; i++) {
29
+ var g = busy[i][0] - busy[i - 1][1];
30
+ if (g > longest) { longest = g; at = busy[i - 1][1]; }
31
+ }
32
+ var peak = 0;
33
+ iv.forEach(function (p) {
34
+ var c = iv.filter(function (o) { return o[0] <= p[0] && o[1] > p[0]; }).length;
35
+ peak = Math.max(peak, c);
36
+ });
37
+ return { iv: iv, start: iv[0][0], span: span, busyMs: busyMs, busyPct: Math.round(busyMs / (span || 1) * 100),
38
+ peak: peak, longest: longest, longestAt: at };
39
+ }
40
+
41
+ /** Time segments left to right: real stretches plus fixed-width bands for idle gaps over 15 minutes. */
42
+ function flowSegments(iv, width) {
43
+ var merged = flowMerge(iv, TL.GAP);
44
+ var plotW = width - TL.LW - 24 - (merged.length - 1) * TL.GW;
45
+ var active = merged.reduce(function (s, p) { return s + p[1] - p[0]; }, 0) || 1;
46
+ var k = Math.max(plotW, 120) / active, segs = [], x = TL.LW;
47
+ merged.forEach(function (p, i) {
48
+ if (i) {
49
+ segs.push({ gap: true, t0: merged[i - 1][1], t1: p[0], x0: x, x1: x + TL.GW });
50
+ x += TL.GW;
51
+ }
52
+ segs.push({ t0: p[0], t1: p[1], x0: x, x1: x + (p[1] - p[0]) * k });
53
+ x += (p[1] - p[0]) * k;
54
+ });
55
+ var X = function (t) {
56
+ var s = segs.filter(function (q) { return t >= q.t0 && t <= q.t1; })[0] || (t < segs[0].t0 ? segs[0] : segs[segs.length - 1]);
57
+ var scale = s.gap ? TL.GW / (s.t1 - s.t0) : k;
58
+ return s.x0 + Math.max(0, Math.min(t, s.t1) - s.t0) * scale;
59
+ };
60
+ return { segs: segs, k: k, X: X, right: x };
61
+ }
62
+
63
+ /** Overlapping tasks of one agent go on extra rows. */
64
+ function flowRows(list, now) {
65
+ var rows = [], place = {};
66
+ list.slice().sort(function (a, b) { return a.startT - b.startT; }).forEach(function (t) {
67
+ var r = 0;
68
+ while (r < rows.length && rows[r] > t.startT) r++;
69
+ rows[r] = flowEnd(t, now);
70
+ place[t.id] = r;
71
+ });
72
+ return { count: Math.max(1, rows.length), place: place };
73
+ }
74
+
75
+ function flowTimeModel(m, now, outer) {
76
+ var timed = m.tasks.filter(function (t) { return t.startT; });
77
+ if (!timed.length) return null;
78
+ var sum = flowSummary(timed, now);
79
+ var width = Math.max(760, (outer || 1000) - 2);
80
+ var sg = flowSegments(sum.iv, width);
81
+ var lanes = [], y = TL.TOPY;
82
+ m.tasks.forEach(function (t) {
83
+ var a = flowAgentKey(t);
84
+ if (lanes.some(function (l) { return l.key === a; })) return;
85
+ var list = timed.filter(function (o) { return flowAgentKey(o) === a; });
86
+ if (!list.length) return;
87
+ var rows = flowRows(list, now);
88
+ lanes.push({ key: a, y: y, h: rows.count * TL.ROWH + 12, place: rows.place, tasks: list });
89
+ y += rows.count * TL.ROWH + 12;
90
+ });
91
+ return { timed: timed, untimed: m.tasks.filter(function (t) { return !t.startT; }), sum: sum, sg: sg,
92
+ lanes: lanes, width: width, height: y + 8, now: now };
93
+ }
94
+
95
+ `;
@@ -0,0 +1 @@
1
+ export declare const flowTimelineScript: string;