fraim-framework 2.0.162 → 2.0.164

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 (33) hide show
  1. package/dist/src/ai-hub/desktop-main.js +4 -1
  2. package/dist/src/ai-hub/hosts.js +97 -12
  3. package/dist/src/ai-hub/preferences.js +1 -1
  4. package/dist/src/ai-hub/server.js +49 -123
  5. package/dist/src/cli/commands/init-project.js +15 -14
  6. package/dist/src/cli/commands/sync.js +38 -0
  7. package/dist/src/cli/doctor/check-runner.js +3 -1
  8. package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +261 -2
  9. package/dist/src/cli/utils/git-org-sync.js +56 -0
  10. package/dist/src/cli/utils/org-migration.js +50 -0
  11. package/dist/src/cli/utils/org-pack-sync.js +208 -0
  12. package/dist/src/cli/utils/project-bootstrap.js +20 -7
  13. package/dist/src/cli/utils/user-config.js +68 -0
  14. package/dist/src/core/fraim-config-schema.generated.js +10 -0
  15. package/dist/src/first-run/types.js +8 -0
  16. package/dist/src/local-mcp-server/agent-token-prices.js +30 -0
  17. package/dist/src/local-mcp-server/learning-context-builder.js +78 -29
  18. package/dist/src/local-mcp-server/stdio-server.js +30 -0
  19. package/index.js +1 -1
  20. package/package.json +2 -3
  21. package/public/ai-hub/index.html +5 -5
  22. package/public/ai-hub/powerpoint-taskpane/index.html +236 -236
  23. package/public/ai-hub/powerpoint-taskpane/manifest.xml +29 -29
  24. package/public/ai-hub/review.css +15 -15
  25. package/public/ai-hub/script.js +254 -195
  26. package/public/ai-hub/styles.css +206 -16
  27. package/public/first-run/styles.css +73 -73
  28. package/dist/src/ai-hub/word-sideload.js +0 -95
  29. package/dist/src/cli/commands/test-mcp.js +0 -171
  30. package/dist/src/cli/setup/first-run.js +0 -242
  31. package/dist/src/core/config-writer.js +0 -75
  32. package/dist/src/core/utils/job-aliases.js +0 -47
  33. package/dist/src/core/utils/workflow-parser.js +0 -174
@@ -1,5 +1,7 @@
1
1
  :root {
2
2
  color-scheme: light dark;
3
+ /* #ai-hub-polish: allow block-size:auto to animate (accordion open/close). */
4
+ interpolate-size: allow-keywords;
3
5
  /* Light theme — Apple-style near-white */
4
6
  --bg: #f5f5f7;
5
7
  --surface: #ffffff;
@@ -495,6 +497,19 @@ img.conv-employee-avatar {
495
497
  background: var(--accent-soft);
496
498
  border-color: rgba(0,113,227,.28);
497
499
  }
500
+ /* Issue #550: Watercooler Conversations group modifier */
501
+ /* R4: dashed avatar placeholder — no image or persona initials */
502
+ .conv-employee-avatar--adhoc {
503
+ border-style: dashed;
504
+ border-color: var(--muted);
505
+ background: transparent;
506
+ color: transparent;
507
+ }
508
+ /* R5: muted label color for the Watercooler group header */
509
+ .conv-employee-group--adhoc .conv-employee-tab-label {
510
+ color: var(--muted);
511
+ font-weight: 600;
512
+ }
498
513
  .conv-employee-list {
499
514
  display: grid;
500
515
  gap: 8px;
@@ -588,6 +603,8 @@ img.conv-employee-avatar {
588
603
  .conv-status.working { color: var(--state-working); }
589
604
  .conv-status.waiting { color: var(--state-waiting); }
590
605
  .conv-status.complete { color: var(--state-complete); }
606
+ /* #549 R4: Amber label for manager-stopped runs in the conversation rail. */
607
+ .conv-status.stopped { color: var(--warn); }
591
608
  .state-dot {
592
609
  display: inline-block;
593
610
  width: 8px;
@@ -765,6 +782,11 @@ img.conv-employee-avatar {
765
782
  background: color-mix(in srgb, var(--done) 14%, transparent);
766
783
  color: var(--done);
767
784
  }
785
+ /* #549 R3: Amber STOPPED pill — intentional pause, distinct from red WAITING ON YOU. */
786
+ .run-state-pill.stopped {
787
+ background: color-mix(in srgb, var(--warn) 15%, transparent);
788
+ color: var(--warn);
789
+ }
768
790
  .run-state-pill.approved {
769
791
  background: color-mix(in srgb, #2e7d32 18%, transparent);
770
792
  color: #2e7d32;
@@ -835,6 +857,11 @@ img.conv-employee-avatar {
835
857
  flex-shrink: 0;
836
858
  }
837
859
  .panel-details[open] > summary::before { transform: rotate(90deg); }
860
+ .panel-details:not([open]) > .panel-body,
861
+ .panel-details:not([open]) > .messages,
862
+ .panel-details:not([open]) > .micro-log {
863
+ display: none;
864
+ }
838
865
  .panel-summary-copy {
839
866
  display: flex;
840
867
  flex-direction: column;
@@ -1170,6 +1197,16 @@ img.coach-employee-avatar { object-fit: cover; border-radius: 4px; }
1170
1197
  }
1171
1198
  .typing-dot:nth-child(2) { animation-delay: 120ms; }
1172
1199
  .typing-dot:nth-child(3) { animation-delay: 240ms; }
1200
+ /* #549 R1: Static dash shown in the working-indicator bubble while stop is in-flight.
1201
+ Replaces the animated typing-dots to signal "stopping in progress, please wait". */
1202
+ .stopping-dash {
1203
+ display: inline-flex;
1204
+ align-items: center;
1205
+ justify-content: center;
1206
+ font-size: 14px;
1207
+ color: var(--muted);
1208
+ line-height: 1;
1209
+ }
1173
1210
  @keyframes typing-bounce {
1174
1211
  0%, 80%, 100% { opacity: 0.35; transform: translateY(0); }
1175
1212
  40% { opacity: 0.9; transform: translateY(-3px); }
@@ -1284,13 +1321,18 @@ img.coach-employee-avatar { object-fit: cover; border-radius: 4px; }
1284
1321
 
1285
1322
  .micro {
1286
1323
  margin-top: 0;
1324
+ border-color: color-mix(in srgb, var(--accent) 38%, var(--line));
1325
+ background: color-mix(in srgb, var(--accent-soft) 48%, var(--soft));
1287
1326
  /* Removed: position:sticky / bottom:0 / z-index:2 — sticky caused the
1288
1327
  micro-manage label to float over the coach section in the flex column.
1289
1328
  It's now a normal flow element at the bottom of the support-stack. */
1290
1329
  }
1330
+ .micro .panel-kicker {
1331
+ color: var(--accent-strong);
1332
+ }
1291
1333
  .micro summary {
1292
1334
  cursor: pointer;
1293
- color: var(--muted);
1335
+ color: var(--text);
1294
1336
  font-size: 14px;
1295
1337
  list-style: none;
1296
1338
  display: flex;
@@ -2374,7 +2416,7 @@ body.hub-shell { display: flex; flex-direction: column; height: 100vh; overflow:
2374
2416
  .syn-row { display: flex; align-items: flex-start; gap: 14px; padding: 12px 18px; border-bottom: 1px solid var(--line); }
2375
2417
  .syn-row:last-child { border-bottom: none; }
2376
2418
  .syn-label { font-size: 12px; font-weight: 600; color: var(--muted); width: 90px; flex-shrink: 0; padding-top: 1px; }
2377
- .syn-val { flex: 1; font-size: 13px; color: var(--text); line-height: 1.5; }
2419
+ .syn-val { flex: 1; min-width: 0; overflow-wrap: break-word; font-size: 13px; color: var(--text); line-height: 1.5; }
2378
2420
  .syn-edit { font-size: 12px; color: var(--accent); font-weight: 500; background: none; border: none; cursor: pointer; flex-shrink: 0; }
2379
2421
 
2380
2422
  /* Learnings */
@@ -2437,7 +2479,22 @@ body.hub-shell { display: flex; flex-direction: column; height: 100vh; overflow:
2437
2479
  .ctx-acc > summary .ca-chev { font-size: 10px; color: var(--muted); transition: transform .15s; }
2438
2480
  .ctx-acc[open] > summary .ca-chev { transform: rotate(90deg); }
2439
2481
  .ctx-acc > summary .ca-note { font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 11px; color: var(--muted); }
2440
- .ctx-acc-body { padding: 4px 14px 14px; }
2482
+ /* #ai-hub-polish F2: a long section must not bury the others. Cap the body and
2483
+ let it scroll internally so every accordion header stays reachable. */
2484
+ .ctx-acc-body { padding: 4px 14px 14px; max-height: min(62vh, 620px); overflow-y: auto; }
2485
+
2486
+ /* #ai-hub-polish F1: ease the open/close instead of snapping. Animate the
2487
+ <details> content slot; interpolate-size lets block-size animate to/from auto.
2488
+ @supports keeps older engines on the instant (pre-polish) behavior, and the
2489
+ prefers-reduced-motion block above neutralizes the transition. */
2490
+ @supports selector(::details-content) {
2491
+ .ctx-acc::details-content {
2492
+ block-size: 0;
2493
+ overflow: clip;
2494
+ transition: block-size .24s ease, content-visibility .24s allow-discrete;
2495
+ }
2496
+ .ctx-acc[open]::details-content { block-size: auto; }
2497
+ }
2441
2498
 
2442
2499
  /* #521: project-onboarding loop banner inside the Brief — "team is processing /
2443
2500
  here's their understanding to review". */
@@ -2582,6 +2639,9 @@ body.hub-shell { display: flex; flex-direction: column; height: 100vh; overflow:
2582
2639
  .dot-amber { background: var(--state-working) !important; }
2583
2640
  .dot-green { background: var(--state-complete) !important; }
2584
2641
  .dot-grey { background: var(--line) !important; }
2642
+ /* #549 R4: amber-static — same amber colour as dot-amber but no pulse animation.
2643
+ Used for manager-stopped runs to distinguish intentional pause from active work. */
2644
+ .dot-amber-static { background: var(--warn) !important; }
2585
2645
 
2586
2646
  /* Workspace conversation pane */
2587
2647
  .workspace-conv { flex: 1; display: flex; flex-direction: column; overflow: hidden; background: var(--bg); }
@@ -2606,26 +2666,115 @@ body.hub-shell { display: flex; flex-direction: column; height: 100vh; overflow:
2606
2666
  padding: 16px 18px;
2607
2667
  }
2608
2668
 
2609
- /* Conversation view (full height): thread gets priority with a usable floor and
2610
- scrolls its messages internally; the support-stack stacks naturally and scrolls
2611
- on its own when both coach + micro-manage are open (it must NOT be a height-
2612
- distributing grid, or it clips the open panels the original bug). */
2669
+ /* Conversation view (full height): the open panels SHARE the available real
2670
+ estate instead of being pinned to fixed caps. Each open panel grows to fill its
2671
+ slice and scrolls internally when its content overflows (its own scrollbar).
2672
+ #active-conv keeps overflow-y:auto only as a fallback for very short windows
2673
+ where the panels' min-heights can't all fit. */
2613
2674
  .workspace-conv #active-conv {
2675
+ overflow-y: auto;
2676
+ display: flex;
2677
+ flex-direction: column;
2678
+ gap: 8px;
2679
+ min-height: 0;
2680
+ }
2681
+ .workspace-conv #active-conv > .conv-topline,
2682
+ .workspace-conv #active-conv > .conversation-status { flex: 0 0 auto; }
2683
+
2684
+ /* Manager/employee thread — the primary conversation. The "Ready for review"
2685
+ card lives inside #messages, so it scrolls with the thread. Open thread grows
2686
+ to fill spare height; collapsed it is just its summary bar. */
2687
+ .workspace-conv #thread-panel { flex: 0 0 auto; min-height: 0; }
2688
+ /* Open thread is the sole space-filler: it grows to take all height the support
2689
+ stack does not need, and scrolls #messages internally. Collapsed it is just
2690
+ its summary bar. */
2691
+ .workspace-conv #thread-panel[open] {
2692
+ flex: 1 1 0;
2693
+ display: flex;
2694
+ flex-direction: column;
2695
+ min-height: 120px;
2696
+ }
2697
+ .workspace-conv #thread-panel > summary { flex: 0 0 auto; }
2698
+ /* Chromium wraps <details> content in a ::details-content slot, so #messages is
2699
+ not a direct flex child of the panel. Make the slot a column flex container
2700
+ that fills the panel; then #messages can flex-grow and scroll inside it. */
2701
+ .workspace-conv #thread-panel[open]::details-content {
2702
+ flex: 1 1 0;
2703
+ min-height: 0;
2704
+ display: flex;
2705
+ flex-direction: column;
2614
2706
  overflow: hidden;
2615
- display: grid;
2616
- grid-template-rows: auto auto minmax(200px, 1fr) auto;
2617
2707
  }
2618
- .workspace-conv #thread-panel[open] { min-height: 0; display: flex; flex-direction: column; }
2619
- .workspace-conv #thread-panel > summary { flex-shrink: 0; }
2620
- .workspace-conv #thread-panel #messages { flex: 1; min-height: 0; overflow-y: auto; }
2708
+ .workspace-conv #thread-panel[open] #messages {
2709
+ flex: 1 1 0;
2710
+ min-height: 0;
2711
+ max-height: none;
2712
+ overflow-y: auto;
2713
+ }
2714
+ .workspace-conv #thread-panel:not([open]) #messages { display: none; }
2715
+
2716
+ /* Support stack: Coach (input) + Micro-manage (log). It sizes to its content and
2717
+ never grows past it, so the thread keeps the spare real estate. Each open body
2718
+ has a viewport-relative cap and scrolls internally, so it shows more on bigger
2719
+ screens (and far more than the old 1-line peek) without crowding the thread. */
2621
2720
  .workspace-conv .support-stack {
2622
- display: block; /* override base display:grid so panels stack, not clip */
2721
+ display: flex;
2722
+ flex-direction: column;
2723
+ gap: 8px;
2623
2724
  min-height: 0;
2624
- max-height: 48vh; /* bounded; scrolls internally if both panels open */
2725
+ flex: 0 1 auto;
2726
+ }
2727
+ .workspace-conv .support-stack > .panel-details--coach { flex: 0 0 auto; }
2728
+ .workspace-conv .support-stack > .panel-details--coach[open] > .panel-body {
2729
+ max-height: clamp(140px, 24vh, 260px);
2730
+ overflow-y: auto;
2731
+ }
2732
+ .workspace-conv .panel-details--coach .panel-body { padding-bottom: 8px; }
2733
+ .workspace-conv .coach-input textarea {
2734
+ min-height: 44px;
2735
+ padding-top: 9px;
2736
+ padding-bottom: 9px;
2737
+ }
2738
+ .workspace-conv .coach-note { display: none; }
2739
+ .workspace-conv .quick-coach-row { gap: 5px; margin-bottom: 8px; }
2740
+ .workspace-conv .quick-coach-btn { padding: 4px 9px; }
2741
+
2742
+ .workspace-conv .support-stack > .micro { flex: 0 0 auto; min-height: 0; }
2743
+ .workspace-conv .support-stack > .micro[open] > .micro-log {
2744
+ max-height: clamp(120px, 24vh, 260px);
2625
2745
  overflow-y: auto;
2626
2746
  }
2627
2747
  .workspace-conv .support-stack > .panel-details,
2628
- .workspace-conv .support-stack > .micro { margin-bottom: 8px; }
2748
+ .workspace-conv .support-stack > .micro { margin-bottom: 0; }
2749
+
2750
+ @media (max-width: 820px) {
2751
+ /* On narrow screens drop the fill model: panels flow naturally and the page
2752
+ scrolls, with each open body keeping a viewport-relative scroll cap. */
2753
+ .workspace-conv #active-conv {
2754
+ display: flex;
2755
+ flex-direction: column;
2756
+ overflow: visible;
2757
+ }
2758
+ .workspace-conv #thread-panel[open],
2759
+ .workspace-conv .support-stack,
2760
+ .workspace-conv .support-stack:has(.micro[open]),
2761
+ .workspace-conv .support-stack > .micro[open] {
2762
+ flex: 0 0 auto;
2763
+ display: block;
2764
+ min-height: 0;
2765
+ }
2766
+ .workspace-conv #thread-panel[open] #messages,
2767
+ .workspace-conv .support-stack > .micro[open] > .micro-log {
2768
+ flex: initial;
2769
+ height: auto;
2770
+ max-height: clamp(140px, 32vh, 280px);
2771
+ overflow-y: auto;
2772
+ }
2773
+ .workspace-conv .support-stack > .panel-details[open] > .panel-body {
2774
+ max-height: clamp(140px, 32vh, 280px);
2775
+ overflow-y: auto;
2776
+ }
2777
+ }
2629
2778
 
2630
2779
  @media (max-width: 640px) {
2631
2780
  body.hub-shell { overflow-x: hidden; }
@@ -2852,6 +3001,39 @@ body.hub-shell { display: flex; flex-direction: column; height: 100vh; overflow:
2852
3001
  .workspace-conv > .page { overflow: hidden; }
2853
3002
  .workspace-conv .layout { display: flex; gap: 0; min-height: 0; flex: 1; overflow: hidden; }
2854
3003
 
3004
+ @media (max-width: 820px) {
3005
+ body.hub-shell {
3006
+ height: auto;
3007
+ min-height: 100vh;
3008
+ overflow: auto;
3009
+ }
3010
+ .hub-area,
3011
+ .area-main,
3012
+ #proj-workspace,
3013
+ .workspace-conv,
3014
+ .workspace-conv > .page,
3015
+ .workspace-conv .layout {
3016
+ overflow: visible;
3017
+ }
3018
+ }
3019
+
3020
+ /* #ai-hub-polish F3: Company & Manager areas had no narrow-width layout — the
3021
+ fixed 244px rail pushed the main column off the right edge (horizontal scroll).
3022
+ Below the Projects-workspace breakpoint, stack the rail above the content and
3023
+ let the page shrink so nothing overflows. 2-column stays intact on tablets. */
3024
+ @media (max-width: 640px) {
3025
+ body.hub-shell { overflow-x: hidden; }
3026
+ .area-shell { flex-direction: column; }
3027
+ .area-rail {
3028
+ width: 100%;
3029
+ flex-shrink: 0;
3030
+ max-height: 200px;
3031
+ border-right: none;
3032
+ border-bottom: 1px solid var(--line);
3033
+ }
3034
+ .hub-area-page { padding: 22px 16px 40px; min-width: 0; max-width: 100%; }
3035
+ }
3036
+
2855
3037
 
2856
3038
  /* ── #512 Round 2: actual class names from rendered HTML ────────────────── */
2857
3039
 
@@ -2866,8 +3048,16 @@ body.hub-shell { display: flex; flex-direction: column; height: 100vh; overflow:
2866
3048
  .syn-row { display: flex; align-items: flex-start; gap: 14px; padding: 13px 18px; border-bottom: 1px solid var(--line); }
2867
3049
  .syn-row:last-child { border-bottom: none; }
2868
3050
  .syn-label { font-size: 12px; font-weight: 600; color: var(--muted); width: 88px; flex-shrink: 0; padding-top: 2px; }
2869
- .syn-val { flex: 1; font-size: 13px; color: var(--text); line-height: 1.5; }
3051
+ .syn-val { flex: 1; min-width: 0; overflow-wrap: break-word; font-size: 13px; color: var(--text); line-height: 1.5; }
2870
3052
  .syn-edit { font-size: 12px; color: var(--accent); font-weight: 500; background: none; border: none; cursor: pointer; flex-shrink: 0; }
3053
+ /* #ai-hub-polish: at phone widths the fixed label column starves the value and
3054
+ forces mid-word wraps. Stack label over value so the value gets full width.
3055
+ Placed after the duplicate .syn-* block above so source order lets it win. */
3056
+ @media (max-width: 560px) {
3057
+ .syn-row { flex-wrap: wrap; gap: 4px 12px; }
3058
+ .syn-label { width: 100%; }
3059
+ .syn-val { flex: 1 1 100%; }
3060
+ }
2871
3061
 
2872
3062
  /* Issue #512 R3/R8 — Team Context inline editor. */
2873
3063
  /* ctx-content is now a div with rendered markdown (formatEmployeeText), not a raw <pre> */
@@ -421,14 +421,14 @@ body {
421
421
  flex-direction: column;
422
422
  }
423
423
 
424
- .recruit-container,
425
- .start-container {
426
- gap: 12px;
427
- }
428
-
429
- .start-container {
430
- gap: 14px;
431
- }
424
+ .recruit-container,
425
+ .start-container {
426
+ gap: 12px;
427
+ }
428
+
429
+ .start-container {
430
+ gap: 14px;
431
+ }
432
432
 
433
433
  .user-type-card {
434
434
  background: var(--surface);
@@ -448,23 +448,23 @@ body {
448
448
  border-color: var(--accent);
449
449
  background: var(--accent-soft);
450
450
  }
451
- .user-type-card--featured:hover {
452
- box-shadow: 0 4px 20px rgba(61, 138, 110, 0.18);
453
- }
454
-
455
- .user-type-card--ide-default {
456
- padding: 24px;
457
- gap: 18px;
458
- }
459
-
460
- .user-type-card--compact {
461
- padding: 16px 18px;
462
- gap: 12px;
463
- }
464
-
465
- .user-type-card--alpha {
466
- background: linear-gradient(180deg, #fbfcfa 0%, #f4f7f2 100%);
467
- }
451
+ .user-type-card--featured:hover {
452
+ box-shadow: 0 4px 20px rgba(61, 138, 110, 0.18);
453
+ }
454
+
455
+ .user-type-card--ide-default {
456
+ padding: 24px;
457
+ gap: 18px;
458
+ }
459
+
460
+ .user-type-card--compact {
461
+ padding: 16px 18px;
462
+ gap: 12px;
463
+ }
464
+
465
+ .user-type-card--alpha {
466
+ background: linear-gradient(180deg, #fbfcfa 0%, #f4f7f2 100%);
467
+ }
468
468
 
469
469
  .card-header {
470
470
  display: flex;
@@ -492,38 +492,38 @@ body {
492
492
  border-color: var(--accent);
493
493
  }
494
494
 
495
- .card-title {
496
- display: block;
497
- font-size: 15px;
498
- font-weight: 600;
499
- color: var(--text);
500
- margin-bottom: 3px;
501
- }
502
- .card-desc { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.45; }
503
-
504
- .card-eyebrow {
505
- display: inline-flex;
506
- align-self: flex-start;
507
- padding: 4px 10px;
508
- border-radius: 999px;
509
- background: var(--accent-soft);
510
- color: var(--accent-strong);
511
- font-size: 11px;
512
- font-weight: 700;
513
- letter-spacing: 0.06em;
514
- text-transform: uppercase;
515
- }
516
-
517
- .route-note {
518
- margin: 0;
519
- color: var(--muted);
520
- font-size: 13px;
521
- line-height: 1.5;
522
- }
523
-
524
- .route-note--compact {
525
- font-size: 12px;
526
- }
495
+ .card-title {
496
+ display: block;
497
+ font-size: 15px;
498
+ font-weight: 600;
499
+ color: var(--text);
500
+ margin-bottom: 3px;
501
+ }
502
+ .card-desc { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.45; }
503
+
504
+ .card-eyebrow {
505
+ display: inline-flex;
506
+ align-self: flex-start;
507
+ padding: 4px 10px;
508
+ border-radius: 999px;
509
+ background: var(--accent-soft);
510
+ color: var(--accent-strong);
511
+ font-size: 11px;
512
+ font-weight: 700;
513
+ letter-spacing: 0.06em;
514
+ text-transform: uppercase;
515
+ }
516
+
517
+ .route-note {
518
+ margin: 0;
519
+ color: var(--muted);
520
+ font-size: 13px;
521
+ line-height: 1.5;
522
+ }
523
+
524
+ .route-note--compact {
525
+ font-size: 12px;
526
+ }
527
527
 
528
528
  .btn {
529
529
  display: inline-flex;
@@ -572,9 +572,9 @@ body {
572
572
  border-color: var(--line);
573
573
  }
574
574
 
575
- /* IDE command display — shown after choosing "In my IDE". */
576
- .cmd-block {
577
- background: #0d1410;
575
+ /* IDE command display — shown after choosing "In my IDE". */
576
+ .cmd-block {
577
+ background: #0d1410;
578
578
  color: #c8d3cd;
579
579
  font-family: "JetBrains Mono", "Cascadia Code", Consolas, monospace;
580
580
  font-size: 14px;
@@ -612,16 +612,16 @@ body {
612
612
  * want the surface to degrade gracefully for users who happen to open it
613
613
  * in a narrow window. Stack the label / verb / change link vertically and
614
614
  * let the row breathe. */
615
- @media (max-width: 600px) {
616
- .page { padding: 24px 16px 24px; }
617
- .checklist .row { gap: 8px; padding: 12px 14px; }
618
- .row .label { min-width: 0; }
619
- .row .verb { flex: 1 1 100%; }
620
- .row .change-link { margin-left: auto; }
621
- .row .project-picker { gap: 8px; }
622
- .row .project-picker input { width: 100%; min-width: 0; }
623
- .user-type-card--ide-default,
624
- .user-type-card--compact {
625
- padding: 18px 16px;
626
- }
627
- }
615
+ @media (max-width: 600px) {
616
+ .page { padding: 24px 16px 24px; }
617
+ .checklist .row { gap: 8px; padding: 12px 14px; }
618
+ .row .label { min-width: 0; }
619
+ .row .verb { flex: 1 1 100%; }
620
+ .row .change-link { margin-left: auto; }
621
+ .row .project-picker { gap: 8px; }
622
+ .row .project-picker input { width: 100%; min-width: 0; }
623
+ .user-type-card--ide-default,
624
+ .user-type-card--compact {
625
+ padding: 18px 16px;
626
+ }
627
+ }
@@ -1,95 +0,0 @@
1
- "use strict";
2
- /**
3
- * Sideloads the Word add-in manifest so it appears in Word's Developer Add-ins
4
- * list without admin rights or AppSource publishing.
5
- *
6
- * Windows: writes a registry value under HKCU\SOFTWARE\Microsoft\Office\16.0\WEF\Developer
7
- * macOS: writes an entry to ~/Library/Containers/com.microsoft.Word/Data/Documents/wef/
8
- *
9
- * Both paths are non-admin and survive app updates (keyed by manifest GUID).
10
- * Safe to call multiple times — checks before writing.
11
- */
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.isSideloaded = isSideloaded;
17
- exports.sideloadManifest = sideloadManifest;
18
- exports.removeSideload = removeSideload;
19
- const fs_1 = __importDefault(require("fs"));
20
- const path_1 = __importDefault(require("path"));
21
- const os_1 = __importDefault(require("os"));
22
- const child_process_1 = require("child_process");
23
- const MANIFEST_GUID = 'd1090951-50cf-4cf2-9d12-b0f8541d265c';
24
- function resolveManifestPath(projectPath) {
25
- const candidates = [
26
- path_1.default.resolve(projectPath, 'extensions/office-word/manifest.xml'),
27
- path_1.default.resolve(__dirname, '..', '..', 'extensions/office-word/manifest.xml'),
28
- path_1.default.resolve(__dirname, '..', '..', '..', 'extensions/office-word/manifest.xml'),
29
- ];
30
- return candidates.find(c => fs_1.default.existsSync(c)) ?? null;
31
- }
32
- function isSideloaded() {
33
- if (process.platform === 'win32') {
34
- const result = (0, child_process_1.spawnSync)('reg', [
35
- 'query',
36
- `HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
37
- '/v', MANIFEST_GUID,
38
- ], { encoding: 'utf8' });
39
- return result.status === 0 && result.stdout.includes(MANIFEST_GUID);
40
- }
41
- if (process.platform === 'darwin') {
42
- const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
43
- return fs_1.default.existsSync(path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`));
44
- }
45
- return false;
46
- }
47
- function sideloadManifest(projectPath) {
48
- const manifestPath = resolveManifestPath(projectPath);
49
- if (!manifestPath) {
50
- return { ok: false, reason: 'Manifest file not found — is extensions/office-word/ present?' };
51
- }
52
- if (process.platform === 'win32') {
53
- const result = (0, child_process_1.spawnSync)('reg', [
54
- 'add',
55
- `HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
56
- '/v', MANIFEST_GUID,
57
- '/t', 'REG_SZ',
58
- '/d', manifestPath,
59
- '/f',
60
- ], { encoding: 'utf8' });
61
- if (result.status !== 0) {
62
- return { ok: false, reason: result.stderr || 'reg add failed' };
63
- }
64
- return { ok: true };
65
- }
66
- if (process.platform === 'darwin') {
67
- const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
68
- try {
69
- fs_1.default.mkdirSync(wefDir, { recursive: true });
70
- fs_1.default.copyFileSync(manifestPath, path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`));
71
- return { ok: true };
72
- }
73
- catch (err) {
74
- return { ok: false, reason: String(err) };
75
- }
76
- }
77
- return { ok: false, reason: `Unsupported platform: ${process.platform}` };
78
- }
79
- function removeSideload() {
80
- if (process.platform === 'win32') {
81
- (0, child_process_1.spawnSync)('reg', [
82
- 'delete',
83
- `HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
84
- '/v', MANIFEST_GUID,
85
- '/f',
86
- ], { encoding: 'utf8' });
87
- return;
88
- }
89
- if (process.platform === 'darwin') {
90
- const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
91
- const target = path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`);
92
- if (fs_1.default.existsSync(target))
93
- fs_1.default.unlinkSync(target);
94
- }
95
- }