fraim-hub 2.0.332 → 2.0.335
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/ai-hub/configured-agent-command.js +2 -1
- package/dist/src/ai-hub/hosts.js +1 -0
- package/dist/src/ai-hub/hub-install.js +1 -1
- package/dist/src/ai-hub/server.js +9 -9
- package/dist/src/cli/doctor/checks/agent-cli-health-checks.js +1 -1
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +1 -1
- package/dist/src/cli/utils/persist-shell-path.js +1 -1
- package/dist/src/cli/utils/win32-cmd-spawn.js +1 -0
- package/dist/src/first-run/session-service.js +3 -0
- package/package.json +2 -2
- package/public/ai-hub/index.html +25 -49
- package/public/ai-hub/review.css +3 -2
- package/public/ai-hub/script.js +219 -227
- package/public/ai-hub/styles.css +46 -215
|
@@ -80,7 +80,7 @@ function probeCommandResolvesNonInteractively(runner, commandName) {
|
|
|
80
80
|
result = null;
|
|
81
81
|
}
|
|
82
82
|
else {
|
|
83
|
-
const r = (0, child_process_1.spawnSync)('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `Get-Command -Name ${JSON.stringify(commandName)} -ErrorAction Stop`], { encoding: 'utf8', timeout: NON_INTERACTIVE_PROBE_TIMEOUT_MS });
|
|
83
|
+
const r = (0, child_process_1.spawnSync)('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `Get-Command -Name ${JSON.stringify(commandName)} -ErrorAction Stop`], { encoding: 'utf8', timeout: NON_INTERACTIVE_PROBE_TIMEOUT_MS, windowsHide: true });
|
|
84
84
|
result = r.error || r.status !== 0
|
|
85
85
|
? `Command '${commandName}' not found in non-interactive PowerShell (powershell -NoProfile). ` +
|
|
86
86
|
`This command may be a PowerShell profile function or alias that is unavailable without a profile. ` +
|
|
@@ -92,6 +92,7 @@ function probeCommandResolvesNonInteractively(runner, commandName) {
|
|
|
92
92
|
const r = (0, child_process_1.spawnSync)('bash', ['--noprofile', '--norc', '-c', `command -v ${commandName}`], {
|
|
93
93
|
encoding: 'utf8',
|
|
94
94
|
timeout: NON_INTERACTIVE_PROBE_TIMEOUT_MS,
|
|
95
|
+
windowsHide: true,
|
|
95
96
|
});
|
|
96
97
|
result = r.error || r.status !== 0
|
|
97
98
|
? `Command '${commandName}' not found in non-interactive bash (bash --noprofile --norc). ` +
|
package/dist/src/ai-hub/hosts.js
CHANGED
|
@@ -34,6 +34,7 @@ exports.detectCodexCompactTaskFailure = detectCodexCompactTaskFailure;
|
|
|
34
34
|
exports.describeSpawnError = describeSpawnError;
|
|
35
35
|
exports.hostDefaultUnsetEnv = hostDefaultUnsetEnv;
|
|
36
36
|
exports.buildHostSpawnEnv = buildHostSpawnEnv;
|
|
37
|
+
exports.spawnHostProcess = spawnHostProcess;
|
|
37
38
|
exports.findGeminiSessionIdForPrompt = findGeminiSessionIdForPrompt;
|
|
38
39
|
exports.checkFraimMcpAttachReadiness = checkFraimMcpAttachReadiness;
|
|
39
40
|
const crypto_1 = require("crypto");
|
|
@@ -134,7 +134,7 @@ function writeWindowsShortcutScript(lnkPath, target, workingDir = os_1.default.h
|
|
|
134
134
|
'$sc.Save()',
|
|
135
135
|
].join('; ');
|
|
136
136
|
try {
|
|
137
|
-
(0, child_process_1.execFileSync)('powershell', ['-NoProfile', '-NonInteractive', '-Command', ps], { stdio: 'pipe' });
|
|
137
|
+
(0, child_process_1.execFileSync)('powershell', ['-NoProfile', '-NonInteractive', '-Command', ps], { stdio: 'pipe', windowsHide: true });
|
|
138
138
|
}
|
|
139
139
|
catch {
|
|
140
140
|
// PowerShell unavailable (e.g. minimal CI image) — write a plain .cmd launcher as fallback.
|
|
@@ -3176,14 +3176,17 @@ class AiHubServer {
|
|
|
3176
3176
|
resolveRunProjectPath(bodyProjectPath, scope) {
|
|
3177
3177
|
return (0, run_working_directory_1.resolveRunWorkingDirectory)(scope, bodyProjectPath, this.defaultProjectPath());
|
|
3178
3178
|
}
|
|
3179
|
-
//
|
|
3180
|
-
refreshLocalCatalogInBackground(
|
|
3179
|
+
// Refresh at startup/restart, independently of jobs. Sync also updates the MCP launcher record (#1354).
|
|
3180
|
+
refreshLocalCatalogInBackground() {
|
|
3181
3181
|
if (process.env.NODE_ENV === 'test' || this.catalogSyncChild)
|
|
3182
3182
|
return;
|
|
3183
|
-
const args = ['--yes', 'fraim@latest', 'sync',
|
|
3183
|
+
const args = ['--yes', 'fraim@latest', 'sync', '--global'];
|
|
3184
3184
|
try {
|
|
3185
|
-
// Windows npx is a .cmd shim. Only fixed arguments enter cmd
|
|
3186
|
-
const child = catalogSyncSpawn(process.platform === 'win32' ? (process.env.ComSpec || 'cmd.exe') : 'npx', process.platform === 'win32' ? ['/d', '/s', '/c', 'npx', ...args] : args,
|
|
3185
|
+
// Windows npx is a .cmd shim. Only fixed arguments enter cmd.
|
|
3186
|
+
const child = catalogSyncSpawn(process.platform === 'win32' ? (process.env.ComSpec || 'cmd.exe') : 'npx', process.platform === 'win32' ? ['/d', '/s', '/c', 'npx', ...args] : args,
|
|
3187
|
+
// On Windows, detachment lets descendants allocate visible consoles despite
|
|
3188
|
+
// windowsHide. Ignored stdio and unref still keep this refresh non-blocking.
|
|
3189
|
+
{ detached: process.platform !== 'win32', stdio: 'ignore', windowsHide: true });
|
|
3187
3190
|
this.catalogSyncChild = child;
|
|
3188
3191
|
const release = () => {
|
|
3189
3192
|
if (this.catalogSyncChild === child)
|
|
@@ -3243,7 +3246,7 @@ class AiHubServer {
|
|
|
3243
3246
|
});
|
|
3244
3247
|
}
|
|
3245
3248
|
// Refresh the machine-level job list without delaying Hub startup.
|
|
3246
|
-
this.refreshLocalCatalogInBackground(
|
|
3249
|
+
this.refreshLocalCatalogInBackground();
|
|
3247
3250
|
// Update-badge freshness: refresh the npm-latest cache on our own clock, not just when
|
|
3248
3251
|
// a page load or relaunch decision happens to ask for it. Without this, a Hub that's
|
|
3249
3252
|
// resident in the tray for days with no window reload and no tray re-click never learns
|
|
@@ -7051,9 +7054,6 @@ class AiHubServer {
|
|
|
7051
7054
|
? { ...fraimRunEnriched, compareRun: this.enrichRunForResponse(directRun) }
|
|
7052
7055
|
: fraimRunEnriched;
|
|
7053
7056
|
res.status(201).json(responsePayload);
|
|
7054
|
-
// Background sync: refresh the local FRAIM catalog so the next job
|
|
7055
|
-
// picker load sees any jobs that were added or updated since last run.
|
|
7056
|
-
this.refreshLocalCatalogInBackground(false, projectPath);
|
|
7057
7057
|
}
|
|
7058
7058
|
catch (error) {
|
|
7059
7059
|
for (const spawningRun of pendingSpawnRuns) {
|
|
@@ -44,7 +44,7 @@ function probeVersion(commandPath) {
|
|
|
44
44
|
const args = process.platform === 'win32'
|
|
45
45
|
? ['/d', '/s', '/c', `${escapeWindowsArg(commandPath)} --version`]
|
|
46
46
|
: ['--version'];
|
|
47
|
-
const result = (0, child_process_1.spawnSync)(executable, args, { encoding: 'utf8', timeout: 5000 });
|
|
47
|
+
const result = (0, child_process_1.spawnSync)(executable, args, { encoding: 'utf8', timeout: 5000, windowsHide: true });
|
|
48
48
|
if (result.status !== 0 || result.error)
|
|
49
49
|
return null;
|
|
50
50
|
// Issue #1256 (AC-D1/D2): only stdout counts as a version — see versionFromProbeOutput.
|
|
@@ -93,7 +93,7 @@ function runCommand(command, args, options) {
|
|
|
93
93
|
return spawnSync(command, args, options);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
return spawnSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', [command, ...args.map(quoteCmdArg)].join(' ')], options);
|
|
96
|
+
return spawnSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', [command, ...args.map(quoteCmdArg)].join(' ')], { ...options, windowsHide: true });
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function ensureDir(dir) {
|
|
@@ -59,7 +59,7 @@ function persistShellPath() {
|
|
|
59
59
|
// getUserFraimDirPath() in project-fraim-paths.ts) rather than a
|
|
60
60
|
// dedicated flag for this call site.
|
|
61
61
|
if (process.env.FRAIM_TEST_SANDBOX !== '1') {
|
|
62
|
-
const result = (0, child_process_1.spawnSync)('powershell', ['-NoProfile', '-NonInteractive', '-Command', psCmd], { encoding: 'utf8', timeout: 10000 });
|
|
62
|
+
const result = (0, child_process_1.spawnSync)('powershell', ['-NoProfile', '-NonInteractive', '-Command', psCmd], { encoding: 'utf8', timeout: 10000, windowsHide: true });
|
|
63
63
|
if (result.error || result.status !== 0) {
|
|
64
64
|
throw new Error(`Could not register managed binaries on the User PATH: ${result.error?.message || result.stderr}`);
|
|
65
65
|
}
|
|
@@ -19,6 +19,7 @@ function spawnViaCmdExe(command, args, options) {
|
|
|
19
19
|
const cmdString = needsOuterQuote ? `"${innerCmd}"` : innerCmd;
|
|
20
20
|
return (0, child_process_1.spawnSync)(comSpec, ['/d', '/s', '/c', cmdString], {
|
|
21
21
|
...options,
|
|
22
|
+
windowsHide: true,
|
|
22
23
|
windowsVerbatimArguments: true,
|
|
23
24
|
});
|
|
24
25
|
}
|
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.FIRST_RUN_ROW_IDS = exports.FirstRunSessionService = void 0;
|
|
40
40
|
exports.commandVersion = commandVersion;
|
|
41
|
+
exports.runProcess = runProcess;
|
|
41
42
|
exports.buildFirstRunHubLaunchArgs = buildFirstRunHubLaunchArgs;
|
|
42
43
|
const fs_1 = __importDefault(require("fs"));
|
|
43
44
|
const os_1 = __importDefault(require("os"));
|
|
@@ -77,6 +78,7 @@ function commandVersion(command, extraBinDirs, basePath) {
|
|
|
77
78
|
const recoveredPath = (0, managed_agent_paths_1.buildRecoveredAgentPath)(pathValue ?? process.env.PATH ?? process.env.Path);
|
|
78
79
|
const env = { ...process.env, PATH: recoveredPath, Path: recoveredPath };
|
|
79
80
|
const result = (0, child_process_1.spawnSync)(executable, args, {
|
|
81
|
+
windowsHide: true,
|
|
80
82
|
encoding: 'utf8',
|
|
81
83
|
timeout: 5000,
|
|
82
84
|
env,
|
|
@@ -133,6 +135,7 @@ function runProcess(command, args, env) {
|
|
|
133
135
|
}
|
|
134
136
|
const child = (0, child_process_1.spawn)(realCmd, realArgs, {
|
|
135
137
|
env: childEnv,
|
|
138
|
+
windowsHide: true,
|
|
136
139
|
shell: false,
|
|
137
140
|
});
|
|
138
141
|
let stdout = '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-hub",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.335",
|
|
4
4
|
"description": "FRAIM Hub local companion package.",
|
|
5
5
|
"author": "Sid Mathur <sid.mathur@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/mathursrus/FRAIM#readme",
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
"electron-updater": "^6.8.9",
|
|
222
222
|
"express": "^5.2.1",
|
|
223
223
|
"extract-zip": "^2.0.1",
|
|
224
|
-
"fraim": "2.0.
|
|
224
|
+
"fraim": "2.0.335",
|
|
225
225
|
"mongodb": "^7.0.0",
|
|
226
226
|
"node-cron": "4.2.1",
|
|
227
227
|
"node-edge-tts": "^1.2.10",
|
package/public/ai-hub/index.html
CHANGED
|
@@ -437,12 +437,12 @@
|
|
|
437
437
|
</div>
|
|
438
438
|
|
|
439
439
|
<div class="manage-panel-shell">
|
|
440
|
-
<
|
|
441
|
-
<
|
|
440
|
+
<section class="panel-details panel-details--thread" id="thread-panel">
|
|
441
|
+
<div class="thread-header">
|
|
442
442
|
<span class="panel-summary-copy">
|
|
443
443
|
<span class="panel-kicker" id="thread-panel-kicker">Manage</span>
|
|
444
444
|
</span>
|
|
445
|
-
</
|
|
445
|
+
</div>
|
|
446
446
|
<!-- Issue #1065: find a passage inside the open conversation (R44-R46). A result that
|
|
447
447
|
promises "+6 more matches" is an unkept promise without this: the manager lands in
|
|
448
448
|
a thread that can run to hundreds of messages with no way to reach the match. -->
|
|
@@ -473,26 +473,13 @@
|
|
|
473
473
|
</div>
|
|
474
474
|
</div>
|
|
475
475
|
</div>
|
|
476
|
-
</
|
|
476
|
+
</section>
|
|
477
477
|
<div class="manage-header-actions">
|
|
478
|
-
<button class="conversation-focus-btn" id="conversation-focus-btn" type="button" aria-label="Expand conversation" title="Expand conversation">↗</button>
|
|
479
478
|
<button class="micro-manage-toggle" id="micro-manage-toggle" type="button" role="switch" aria-checked="false" aria-controls="manage-body"><span>Micro-Manage</span><span class="micro-switch-track" aria-hidden="true"></span></button>
|
|
480
479
|
</div>
|
|
481
480
|
</div>
|
|
482
481
|
|
|
483
482
|
<div class="support-stack">
|
|
484
|
-
<!-- Issue #770: single-run deliverables live here (a stable collapsible
|
|
485
|
-
panel), not pinned to the bottom of the thread. Static-and-hidden,
|
|
486
|
-
populated + toggled by renderDeliverablesPanel. -->
|
|
487
|
-
<details class="panel-details panel-details--deliverables" id="deliverables-panel" hidden>
|
|
488
|
-
<summary>
|
|
489
|
-
<span class="panel-summary-copy">
|
|
490
|
-
<span class="panel-kicker">Deliverables</span>
|
|
491
|
-
<span class="panel-summary-text" id="deliverables-summary"></span>
|
|
492
|
-
</span>
|
|
493
|
-
</summary>
|
|
494
|
-
<div class="panel-body" id="deliverables-body"></div>
|
|
495
|
-
</details>
|
|
496
483
|
<!-- Issue #1292: when this conversation's own agent stops being available,
|
|
497
484
|
say so. The selector keeps holding that agent; it is never quietly
|
|
498
485
|
swapped for another one. Lives outside the composer controls so
|
|
@@ -519,23 +506,23 @@
|
|
|
519
506
|
</div>
|
|
520
507
|
</div>
|
|
521
508
|
|
|
522
|
-
<
|
|
523
|
-
<
|
|
509
|
+
<section class="panel-details panel-details--thread" id="ab-direct-thread-panel">
|
|
510
|
+
<div class="thread-header">
|
|
524
511
|
<span class="panel-summary-copy">
|
|
525
512
|
<span class="panel-kicker">Direct output (raw)</span>
|
|
526
513
|
</span>
|
|
527
|
-
</
|
|
514
|
+
</div>
|
|
528
515
|
<pre class="ab-direct-log" id="ab-direct-log"></pre>
|
|
529
|
-
</
|
|
516
|
+
</section>
|
|
530
517
|
|
|
531
518
|
<div class="support-stack">
|
|
532
|
-
<
|
|
533
|
-
<
|
|
519
|
+
<section class="panel-details panel-details--coach" id="ab-direct-reply-panel">
|
|
520
|
+
<div class="thread-header">
|
|
534
521
|
<span class="panel-summary-copy">
|
|
535
522
|
<span class="panel-kicker">Reply to Direct agent</span>
|
|
536
|
-
<span class="panel-summary-text" id="ab-direct-reply-summary">
|
|
523
|
+
<span class="panel-summary-text" id="ab-direct-reply-summary">Send a follow-up</span>
|
|
537
524
|
</span>
|
|
538
|
-
</
|
|
525
|
+
</div>
|
|
539
526
|
<div class="panel-body">
|
|
540
527
|
<div class="coach">
|
|
541
528
|
<div class="coach-input">
|
|
@@ -544,7 +531,7 @@
|
|
|
544
531
|
</div>
|
|
545
532
|
</div>
|
|
546
533
|
</div>
|
|
547
|
-
</
|
|
534
|
+
</section>
|
|
548
535
|
</div>
|
|
549
536
|
</div>
|
|
550
537
|
|
|
@@ -555,9 +542,7 @@
|
|
|
555
542
|
visible even when no conversation is active"). Only the content
|
|
556
543
|
ABOVE this div changes (idle placeholder, hire strip, thread, or
|
|
557
544
|
the A/B compare panel); the composer itself never opens, closes,
|
|
558
|
-
or relocates
|
|
559
|
-
"expand conversation", handled by openConversationFocus/
|
|
560
|
-
closeConversationFocus alongside #thread-panel. -->
|
|
545
|
+
or relocates. -->
|
|
561
546
|
<div id="persistent-composer">
|
|
562
547
|
<div class="composer-suggestions" id="composer-suggestions" hidden>
|
|
563
548
|
<div id="cp-results" class="cp-results-scroll">
|
|
@@ -589,14 +574,10 @@
|
|
|
589
574
|
<button type="button" id="cp-armed-job-clear" aria-label="Change job">×</button>
|
|
590
575
|
</div>
|
|
591
576
|
<div class="staged-attachments" id="staged-attachments"></div>
|
|
592
|
-
|
|
593
|
-
<button class="rb-approve" type="button" id="review-approve">✓ Approve</button>
|
|
594
|
-
<button class="rb-secondary" type="button" id="review-request-changes">↩ Request changes</button>
|
|
595
|
-
<span class="rb-handoff" id="review-handoff">Comment on the doc to leave inline notes, or type below to coach.</span>
|
|
596
|
-
</div>
|
|
577
|
+
|
|
597
578
|
<div class="coach-input">
|
|
598
579
|
<input type="file" id="attach-file-input" multiple accept="image/png,image/jpeg,image/gif,.pdf,.txt,.log" hidden>
|
|
599
|
-
<textarea id="coach-text" aria-label="Type an instruction" placeholder="Type a job name, /handle, or describe what you need..." data-placeholder-idle="Type a job name, /handle, or describe what you need..." data-placeholder-active="Coach this run, or type new instructions..." data-placeholder-armed="Now type what you want done..."></textarea>
|
|
580
|
+
<textarea id="coach-text" aria-label="Type an instruction" placeholder="Type a job name, /handle, or describe what you need..." data-placeholder-idle="Type a job name, /handle, or describe what you need..." data-placeholder-review="Reply to continue the run, or use Approve / Request changes above..." data-placeholder-active="Coach this run, or type new instructions..." data-placeholder-armed="Now type what you want done..."></textarea>
|
|
600
581
|
<div class="composer-footer">
|
|
601
582
|
<button class="attach-btn" type="button" id="attach-btn" aria-label="Attach a file or screenshot" title="Attach a file or screenshot">📎</button>
|
|
602
583
|
<div class="quick-coach-row composer-coach-actions" id="quick-coach-btns" hidden>
|
|
@@ -683,7 +664,7 @@
|
|
|
683
664
|
</main>
|
|
684
665
|
</div>
|
|
685
666
|
|
|
686
|
-
|
|
667
|
+
|
|
687
668
|
</div>
|
|
688
669
|
|
|
689
670
|
<!-- Issue #1065: search results replace the conversation panel while a query is active,
|
|
@@ -1130,19 +1111,6 @@
|
|
|
1130
1111
|
</div>
|
|
1131
1112
|
</div>
|
|
1132
1113
|
|
|
1133
|
-
<!-- Issue #539: Command palette overlay — replaces 2-step modal as job-launch surface -->
|
|
1134
|
-
<section class="conversation-focus-overlay" id="conversation-focus-overlay" role="dialog" aria-modal="true" aria-labelledby="conversation-focus-title" hidden>
|
|
1135
|
-
<div class="conversation-focus-dialog">
|
|
1136
|
-
<div class="conversation-focus-header">
|
|
1137
|
-
<div class="conversation-focus-copy">
|
|
1138
|
-
<strong id="conversation-focus-title">Focused conversation</strong>
|
|
1139
|
-
<span id="conversation-focus-subtitle">Conversation thread and coaching</span>
|
|
1140
|
-
</div>
|
|
1141
|
-
<button class="conversation-focus-close" id="conversation-focus-close" type="button" aria-label="Close focused conversation">Close</button>
|
|
1142
|
-
</div>
|
|
1143
|
-
<div class="conversation-focus-panels" id="conversation-focus-panels"></div>
|
|
1144
|
-
</div>
|
|
1145
|
-
</section>
|
|
1146
1114
|
|
|
1147
1115
|
<!-- Issue #1657 Treatment 3: #cp-modal is retired as a separate dialog.
|
|
1148
1116
|
The ids below (cp-recent-list, cp-catalog-list, cp-agent-picker, etc.)
|
|
@@ -1265,7 +1233,15 @@
|
|
|
1265
1233
|
</div>
|
|
1266
1234
|
</div>
|
|
1267
1235
|
|
|
1236
|
+
<div id="review-action-storage" hidden>
|
|
1237
|
+
<div class="review-actions" id="review-actions" hidden>
|
|
1238
|
+
<button class="rb-approve" type="button" id="review-approve">✓ Approve</button>
|
|
1239
|
+
<button class="rb-secondary" type="button" id="review-request-changes">↩ Request changes</button>
|
|
1240
|
+
<span class="rb-handoff" id="review-handoff">Comment on the doc to leave inline notes, or type below to coach.</span>
|
|
1241
|
+
</div>
|
|
1242
|
+
</div>
|
|
1268
1243
|
<script src="./script.js?v=dark-status-20260721"></script>
|
|
1269
1244
|
<script src="./remote-ui-loader.js?v=server-driven-ui-20260724"></script>
|
|
1245
|
+
|
|
1270
1246
|
</body>
|
|
1271
1247
|
</html>
|
package/public/ai-hub/review.css
CHANGED
|
@@ -422,8 +422,6 @@
|
|
|
422
422
|
.rev-item .rev-dismiss:hover { background: var(--soft); }
|
|
423
423
|
|
|
424
424
|
/* ── #770: Deliverables panel body spacing for the relocated artifact strip. ── */
|
|
425
|
-
#deliverables-panel .panel-body { padding-top: 8px; }
|
|
426
|
-
#deliverables-panel .rc-artifact-strip { margin: 0; }
|
|
427
425
|
|
|
428
426
|
/* ── R9: project-scoped learnings panel (over the workspace conversation) ── */
|
|
429
427
|
.proj-learnings-panel {
|
|
@@ -633,3 +631,6 @@
|
|
|
633
631
|
flex-direction: column;
|
|
634
632
|
gap: 6px;
|
|
635
633
|
}
|
|
634
|
+
|
|
635
|
+
.review-turn .review-actions { border: 0; margin: 0; padding: 0; }
|
|
636
|
+
.review-turn .rb-handoff { flex-basis: 100%; }
|