@shepai/cli 1.90.0 → 1.90.1-pr267.467b85c

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 (151) hide show
  1. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts +1 -0
  2. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts.map +1 -1
  3. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.js +3 -0
  4. package/dist/src/presentation/web/app/api/attachments/preview/route.d.ts.map +1 -1
  5. package/dist/src/presentation/web/app/api/attachments/preview/route.js +47 -11
  6. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  7. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +18 -1
  8. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map +1 -1
  9. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.js +9 -6
  10. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map +1 -1
  11. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.js +2 -1
  12. package/dist/src/presentation/web/components/common/inline-attachments/index.d.ts +3 -0
  13. package/dist/src/presentation/web/components/common/inline-attachments/index.d.ts.map +1 -0
  14. package/dist/src/presentation/web/components/common/inline-attachments/index.js +1 -0
  15. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.d.ts +20 -0
  16. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.d.ts.map +1 -0
  17. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.js +72 -0
  18. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.stories.d.ts +16 -0
  19. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.stories.d.ts.map +1 -0
  20. package/dist/src/presentation/web/components/common/inline-attachments/inline-attachments.stories.js +46 -0
  21. package/dist/tsconfig.build.tsbuildinfo +1 -1
  22. package/package.json +1 -1
  23. package/web/.next/BUILD_ID +1 -1
  24. package/web/.next/build-manifest.json +2 -2
  25. package/web/.next/fallback-build-manifest.json +2 -2
  26. package/web/.next/prerender-manifest.json +3 -3
  27. package/web/.next/required-server-files.js +1 -1
  28. package/web/.next/required-server-files.json +1 -1
  29. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +18 -18
  30. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  31. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  32. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +21 -21
  33. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  34. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  35. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +14 -14
  36. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  37. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  38. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +18 -18
  39. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  40. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  41. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +21 -21
  42. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  43. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  44. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +14 -14
  45. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  46. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  47. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +14 -14
  48. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  49. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/_global-error.html +2 -2
  51. package/web/.next/server/app/_global-error.rsc +1 -1
  52. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  53. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  54. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  55. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  56. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  57. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  58. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
  60. package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
  61. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  62. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  64. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  65. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  67. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  68. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  70. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  71. package/web/.next/server/chunks/[root-of-the-server]__0b10355c._.js +1 -1
  72. package/web/.next/server/chunks/[root-of-the-server]__0b10355c._.js.map +1 -1
  73. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  74. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  75. package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_9027d30a._.js +1 -1
  76. package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_9027d30a._.js.map +1 -1
  77. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js +1 -1
  78. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js.map +1 -1
  79. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js +1 -1
  80. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js.map +1 -1
  81. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
  82. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
  83. package/web/.next/server/chunks/ssr/[root-of-the-server]__17143953._.js +1 -1
  84. package/web/.next/server/chunks/ssr/[root-of-the-server]__17143953._.js.map +1 -1
  85. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__4be9a9bb._.js +1 -1
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__9de266fa._.js +1 -1
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__9de266fa._.js.map +1 -1
  90. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  91. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  92. package/web/.next/server/chunks/ssr/[root-of-the-server]__b3a4cef4._.js +1 -1
  93. package/web/.next/server/chunks/ssr/[root-of-the-server]__b3a4cef4._.js.map +1 -1
  94. package/web/.next/server/chunks/ssr/[root-of-the-server]__c2173b6d._.js +1 -1
  95. package/web/.next/server/chunks/ssr/[root-of-the-server]__c2173b6d._.js.map +1 -1
  96. package/web/.next/server/chunks/ssr/[root-of-the-server]__f0d8d054._.js +2 -2
  97. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
  98. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
  99. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  100. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  101. package/web/.next/server/chunks/ssr/_16235e5e._.js +1 -1
  102. package/web/.next/server/chunks/ssr/_16235e5e._.js.map +1 -1
  103. package/web/.next/server/chunks/ssr/_3a4c94e3._.js +1 -1
  104. package/web/.next/server/chunks/ssr/_3a4c94e3._.js.map +1 -1
  105. package/web/.next/server/chunks/ssr/_752e01fd._.js +1 -1
  106. package/web/.next/server/chunks/ssr/_752e01fd._.js.map +1 -1
  107. package/web/.next/server/chunks/ssr/{_03115bf1._.js → _82fc7ded._.js} +2 -2
  108. package/web/.next/server/chunks/ssr/{_03115bf1._.js.map → _82fc7ded._.js.map} +1 -1
  109. package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
  110. package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  112. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  113. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  114. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  115. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js +1 -1
  116. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  118. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  119. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  120. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  121. package/web/.next/server/chunks/ssr/src_presentation_web_c4348b8a._.js +2 -2
  122. package/web/.next/server/chunks/ssr/src_presentation_web_c4348b8a._.js.map +1 -1
  123. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  124. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
  125. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  126. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
  127. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  128. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  129. package/web/.next/server/chunks/ssr/src_presentation_web_e4510d10._.js +2 -2
  130. package/web/.next/server/chunks/ssr/src_presentation_web_e4510d10._.js.map +1 -1
  131. package/web/.next/server/pages/500.html +2 -2
  132. package/web/.next/server/server-reference-manifest.js +1 -1
  133. package/web/.next/server/server-reference-manifest.json +28 -28
  134. package/web/.next/static/chunks/{feeb4661ab3e51e6.js → 04cf873db838be27.js} +1 -1
  135. package/web/.next/static/chunks/2a09571d38368a18.css +2 -0
  136. package/web/.next/static/chunks/{6f1adf575d82a1ca.js → 34c1d79101dadb5c.js} +1 -1
  137. package/web/.next/static/chunks/55e7ff9b53257d70.js +1 -0
  138. package/web/.next/static/chunks/{0f48ff9396f6ad8d.js → 68edefc639f2a4e3.js} +2 -2
  139. package/web/.next/static/chunks/{679bde57b488628c.js → 6af0a94127b4f6b4.js} +1 -1
  140. package/web/.next/static/chunks/{ac45184c461dc23b.js → 7b2ddf7d99b1a9cd.js} +1 -1
  141. package/web/.next/static/chunks/{85f19bbdb0d011bc.js → 8b801601d5173382.js} +1 -1
  142. package/web/.next/static/chunks/{c4b4a16a5d8914a6.js → a8f05c16f41dd37a.js} +1 -1
  143. package/web/.next/static/chunks/ade219b21d0d43c4.js +1 -0
  144. package/web/.next/static/chunks/{5d599eee149e1390.js → c3237b6cc45b5493.js} +1 -1
  145. package/web/.next/static/chunks/{016d2a66dc4ecb3d.js → e3990ec4ffc45f88.js} +1 -1
  146. package/web/.next/static/chunks/548adb8fecaaddba.css +0 -2
  147. package/web/.next/static/chunks/5c55da0e28e6cdc8.js +0 -1
  148. package/web/.next/static/chunks/cfaf3920a4eb45e3.js +0 -1
  149. /package/web/.next/static/{04e-HIyL71MGmjPjyT0Bg → h7-EvSzAIVVOwAZ05SB1D}/_buildManifest.js +0 -0
  150. /package/web/.next/static/{04e-HIyL71MGmjPjyT0Bg → h7-EvSzAIVVOwAZ05SB1D}/_clientMiddlewareManifest.json +0 -0
  151. /package/web/.next/static/{04e-HIyL71MGmjPjyT0Bg → h7-EvSzAIVVOwAZ05SB1D}/_ssgManifest.js +0 -0
@@ -11,6 +11,7 @@ export interface RejectionFeedbackData {
11
11
  iteration: number;
12
12
  message: string;
13
13
  phase?: string;
14
+ attachments?: string[];
14
15
  }
15
16
  type GetPhaseTimingsResult = {
16
17
  timings: PhaseTimingData[];
@@ -1 +1 @@
1
- {"version":3,"file":"get-feature-phase-timings.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-feature-phase-timings.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,qBAAqB,GACtB;IAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,iBAAiB,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC1E;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2B9F"}
1
+ {"version":3,"file":"get-feature-phase-timings.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-feature-phase-timings.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,KAAK,qBAAqB,GACtB;IAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,iBAAiB,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC1E;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2B9F"}
@@ -42,6 +42,9 @@ async function readRejectionFeedback(featureId) {
42
42
  iteration: Number(entry.iteration ?? 1),
43
43
  message: String(entry.message ?? ''),
44
44
  phase: entry.phase ? String(entry.phase) : undefined,
45
+ attachments: Array.isArray(entry.attachments)
46
+ ? entry.attachments.map(String)
47
+ : undefined,
45
48
  }));
46
49
  }
47
50
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/presentation/web/app/api/attachments/preview/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsB3C,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA6BrE"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/presentation/web/app/api/attachments/preview/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA8D3C,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA0BrE"}
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from 'next/server';
2
- import { readFile, stat } from 'fs/promises';
3
- import { extname, resolve } from 'path';
2
+ import { readFile, stat, readdir } from 'fs/promises';
3
+ import { basename, extname, join, resolve } from 'path';
4
4
  import { getShepHomeDir } from '../../../../../../../packages/core/src/infrastructure/services/filesystem/shep-directory.service.js';
5
5
  const MIME_MAP = {
6
6
  '.png': 'image/png',
@@ -18,6 +18,44 @@ const MIME_MAP = {
18
18
  '.yaml': 'text/yaml',
19
19
  '.yml': 'text/yaml',
20
20
  };
21
+ /**
22
+ * When the exact path doesn't exist (e.g. pending-<id> was renamed to <slug>),
23
+ * search all sibling attachment directories for the same filename.
24
+ */
25
+ async function findAttachmentFallback(requestedPath, attachmentsRoot) {
26
+ const filename = basename(requestedPath);
27
+ try {
28
+ const dirs = await readdir(attachmentsRoot, { withFileTypes: true });
29
+ for (const entry of dirs) {
30
+ if (!entry.isDirectory())
31
+ continue;
32
+ const candidate = join(attachmentsRoot, entry.name, filename);
33
+ try {
34
+ await stat(candidate);
35
+ return candidate;
36
+ }
37
+ catch {
38
+ // not in this directory
39
+ }
40
+ }
41
+ }
42
+ catch {
43
+ // attachments root doesn't exist
44
+ }
45
+ return null;
46
+ }
47
+ function serveFile(filePath, mimeTypeHint) {
48
+ return readFile(filePath).then((buffer) => {
49
+ const ext = extname(filePath).toLowerCase();
50
+ const contentType = MIME_MAP[ext] ?? mimeTypeHint ?? 'application/octet-stream';
51
+ return new NextResponse(buffer, {
52
+ headers: {
53
+ 'Content-Type': contentType,
54
+ 'Cache-Control': 'private, max-age=3600',
55
+ },
56
+ });
57
+ });
58
+ }
21
59
  export async function GET(request) {
22
60
  const path = request.nextUrl.searchParams.get('path');
23
61
  const mimeTypeHint = request.nextUrl.searchParams.get('mimeType');
@@ -31,17 +69,15 @@ export async function GET(request) {
31
69
  }
32
70
  try {
33
71
  await stat(path);
34
- const buffer = await readFile(path);
35
- const ext = extname(path).toLowerCase();
36
- const contentType = MIME_MAP[ext] ?? mimeTypeHint ?? 'application/octet-stream';
37
- return new NextResponse(buffer, {
38
- headers: {
39
- 'Content-Type': contentType,
40
- 'Cache-Control': 'private, max-age=3600',
41
- },
42
- });
72
+ return await serveFile(path, mimeTypeHint);
43
73
  }
44
74
  catch {
75
+ // File not found at exact path — try fallback search
76
+ // This handles the case where pending-<sessionId>/ was renamed to <featureSlug>/
77
+ const fallbackPath = await findAttachmentFallback(path, attachmentsRoot);
78
+ if (fallbackPath) {
79
+ return await serveFile(fallbackPath, mimeTypeHint);
80
+ }
45
81
  return NextResponse.json({ error: 'File not found' }, { status: 404 });
46
82
  }
47
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAI5E,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAE/F,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE;QACb,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,IAAI,EAAE,OAAO,CAAC;IACd,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+ED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,kGAAkG;IAClG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAoB,EACpB,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,YAAY,GACb,EAAE,wBAAwB,2CA6kB1B"}
1
+ {"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAI5E,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAE/F,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE;QACb,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,IAAI,EAAE,OAAO,CAAC;IACd,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+ED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,kGAAkG;IAClG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAoB,EACpB,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,YAAY,GACb,EAAE,wBAAwB,2CAymB1B"}
@@ -106,9 +106,11 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
106
106
  const [overrideModel, setOverrideModel] = useState(undefined);
107
107
  const [isDragOver, setIsDragOver] = useState(false);
108
108
  const [uploadError, setUploadError] = useState(null);
109
+ const [isPromptFocused, setIsPromptFocused] = useState(false);
109
110
  // Stable sessionId per mount — used for upload dedup grouping
110
111
  const sessionIdRef = useRef(crypto.randomUUID());
111
112
  const dragCounterRef = useRef(0);
113
+ const promptContainerRef = useRef(null);
112
114
  // Sync state when workflowDefaults load asynchronously
113
115
  useEffect(() => {
114
116
  if (workflowDefaults) {
@@ -344,8 +346,23 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
344
346
  formRef.current?.requestSubmit();
345
347
  }
346
348
  }, []);
349
+ const handlePromptFocus = useCallback(() => {
350
+ setIsPromptFocused(true);
351
+ }, []);
352
+ const handlePromptBlur = useCallback(() => {
353
+ // Defer the check so focus has time to settle on the new target.
354
+ // This correctly handles Radix portals (e.g. AgentModelPicker popover) that
355
+ // render outside the container DOM but are logically part of the prompt area.
356
+ setTimeout(() => {
357
+ const withinContainer = promptContainerRef.current?.contains(document.activeElement);
358
+ const pickerPopoverOpen = promptContainerRef.current?.querySelector('[aria-expanded="true"]') !== null;
359
+ if (!withinContainer && !pickerPopoverOpen) {
360
+ setIsPromptFocused(false);
361
+ }
362
+ }, 0);
363
+ }, []);
347
364
  const hasFeatures = features && features.length > 0;
348
- return (_jsx(BaseDrawer, { open: open, onClose: attemptClose, size: "md", modal: false, dismissOnOutsideClick: true, "data-testid": "feature-create-drawer", header: _jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), isSubmitting ? (_jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })) : null] }), footer: _jsxs("div", { className: "flex flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: attemptClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !description.trim() || isSubmitting, children: isSubmitting ? 'Creating...' : '+ Create Feature' })] }), children: _jsx("div", { className: "overflow-y-auto p-4", children: _jsx(TooltipProvider, { delayDuration: 400, children: _jsxs("form", { ref: formRef, id: "create-feature-form", onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs("div", { role: "region", "aria-label": "File drop zone", "data-drag-over": isDragOver ? 'true' : 'false', onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn('flex flex-col gap-1.5 rounded-md border-2 border-transparent p-1 transition-colors', isDragOver && 'border-primary/50 bg-primary/5'), children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIBE YOUR FEATURE" }), _jsxs("div", { className: "border-input focus-within:ring-ring/50 focus-within:border-ring flex h-56 flex-col overflow-hidden rounded-md border shadow-xs transition-[color,box-shadow] focus-within:ring-[3px]", children: [_jsx(Textarea, { id: "feature-description", placeholder: "e.g. Add GitHub OAuth login with callback handling and token refresh...", value: description, onChange: (e) => setDescription(e.target.value), onKeyDown: handleKeyDown, onPaste: handlePaste, required: true, disabled: isSubmitting, className: "min-h-0 flex-1 resize-none rounded-none border-0 shadow-none focus-visible:ring-0" }), attachments.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-1.5 px-3 py-2", children: attachments.map((file) => (_jsx(AttachmentChip, { name: file.name, size: file.size, mimeType: file.mimeType, path: file.path, onRemove: () => handleRemoveFile(file.id), disabled: isSubmitting, loading: file.loading }, file.id))) })), uploadError ? (_jsx("p", { className: "text-destructive px-3 pb-2 text-xs", children: uploadError })) : null, _jsxs("div", { className: "border-input flex items-center gap-3 border-t px-3 py-1.5", children: [_jsx(AgentModelPicker, { initialAgentType: overrideAgent ?? currentAgentType ?? 'claude-code', initialModel: overrideModel ?? currentModel ?? 'claude-sonnet-4-6', mode: "override", onAgentModelChange: (agent, model) => {
365
+ return (_jsx(BaseDrawer, { open: open, onClose: attemptClose, size: "md", modal: false, dismissOnOutsideClick: true, "data-testid": "feature-create-drawer", header: _jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), isSubmitting ? (_jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })) : null] }), footer: _jsxs("div", { className: "flex flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: attemptClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !description.trim() || isSubmitting, children: isSubmitting ? 'Creating...' : '+ Create Feature' })] }), children: _jsx("div", { className: "overflow-y-auto p-4", children: _jsx(TooltipProvider, { delayDuration: 400, children: _jsxs("form", { ref: formRef, id: "create-feature-form", onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs("div", { role: "region", "aria-label": "File drop zone", "data-drag-over": isDragOver ? 'true' : 'false', onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn('flex flex-col gap-1.5 rounded-md border-2 border-transparent p-1 transition-colors', isDragOver && 'border-primary/50 bg-primary/5'), children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIBE YOUR FEATURE" }), _jsxs("div", { ref: promptContainerRef, onFocus: handlePromptFocus, onBlur: handlePromptBlur, className: cn('border-input flex h-56 flex-col overflow-hidden rounded-md border shadow-xs transition-[color,box-shadow]', isPromptFocused && 'ring-ring/50 border-ring ring-[3px]'), children: [_jsx(Textarea, { id: "feature-description", placeholder: "e.g. Add GitHub OAuth login with callback handling and token refresh...", value: description, onChange: (e) => setDescription(e.target.value), onKeyDown: handleKeyDown, onPaste: handlePaste, required: true, disabled: isSubmitting, className: "min-h-0 flex-1 resize-none rounded-none border-0 shadow-none focus-visible:ring-0" }), attachments.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-1.5 px-3 py-2", children: attachments.map((file) => (_jsx(AttachmentChip, { name: file.name, size: file.size, mimeType: file.mimeType, path: file.path, onRemove: () => handleRemoveFile(file.id), disabled: isSubmitting, loading: file.loading }, file.id))) })), uploadError ? (_jsx("p", { className: "text-destructive px-3 pb-2 text-xs", children: uploadError })) : null, _jsxs("div", { className: "border-input flex items-center gap-3 border-t px-3 py-1.5", children: [_jsx(AgentModelPicker, { initialAgentType: overrideAgent ?? currentAgentType ?? 'claude-code', initialModel: overrideModel ?? currentModel ?? 'claude-sonnet-4-6', mode: "override", onAgentModelChange: (agent, model) => {
349
366
  setOverrideAgent(agent);
350
367
  setOverrideModel(model);
351
368
  }, disabled: isSubmitting, className: "w-55" }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "ml-auto flex cursor-pointer items-center gap-2", children: [_jsx(Switch, { id: "fast-mode", checked: fast, onCheckedChange: setFast, disabled: isSubmitting }), _jsxs(Label, { htmlFor: "fast-mode", className: "flex cursor-pointer items-center gap-1 text-sm font-medium", children: [_jsx(Zap, { className: "h-3.5 w-3.5" }), "Fast Mode"] })] }) }), _jsx(TooltipContent, { side: "bottom", children: "Skip SDLC phases and implement directly from your prompt." })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: handleAddFiles, disabled: isSubmitting, "aria-label": "Attach files", className: "text-muted-foreground hover:text-foreground cursor-pointer rounded p-1 transition-colors", children: _jsx(PaperclipIcon, { className: "h-4 w-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: "Attach files" })] })] })] })] }), hasFeatures && initialParentId !== undefined ? (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "parent-feature", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "PARENT FEATURE" }), _jsx(ParentFeatureCombobox, { features: features, value: parentId, onChange: setParentId, disabled: isSubmitting })] })) : null, _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "border-input flex items-center gap-4 rounded-md border px-3 py-2.5", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "text-muted-foreground w-16 shrink-0 cursor-default text-xs font-semibold tracking-wider", children: "APPROVE" }) }), _jsx(TooltipContent, { side: "bottom", children: "Auto-approve phase transitions without manual review." })] }), _jsx("div", { className: "flex flex-1 items-center gap-4", children: AUTO_APPROVE_OPTIONS.map((opt) => (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "flex cursor-pointer items-center gap-1.5", children: [_jsx(Switch, { id: `approve-${opt.id}`, size: "sm", checked: approvalGates[opt.id] ?? false, onCheckedChange: (v) => setApprovalGates((prev) => ({ ...prev, [opt.id]: v })), disabled: isSubmitting ||
@@ -1 +1 @@
1
- {"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yCAAyC,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AAsED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,2CA8D3F"}
1
+ {"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yCAAyC,CAAC;AAGjD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AA4ED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,2CA8D3F"}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Loader2, AlertCircle, Clock } from 'lucide-react';
4
+ import { InlineAttachments } from '../../common/inline-attachments/index.js';
4
5
  const NODE_TO_PHASE = {
5
6
  analyze: 'Analyzing',
6
7
  requirements: 'Requirements',
@@ -38,7 +39,7 @@ function groupTimingsByRun(timings) {
38
39
  return groups;
39
40
  }
40
41
  /**
41
- * Map each run:rejected timing entry to its rejection feedback message.
42
+ * Map each run:rejected timing entry to its rejection feedback message and attachments.
42
43
  * Matches by sequential order: 1st rejected event → 1st feedback entry, etc.
43
44
  */
44
45
  function buildRejectionMessageMap(timings, feedback) {
@@ -49,7 +50,8 @@ function buildRejectionMessageMap(timings, feedback) {
49
50
  for (const t of timings) {
50
51
  if (t.phase === 'run:rejected' && rejectionIdx < feedback.length) {
51
52
  const key = `${t.agentRunId}-${t.phase}-${t.startedAt}`;
52
- map.set(key, feedback[rejectionIdx].message);
53
+ const entry = feedback[rejectionIdx];
54
+ map.set(key, { message: entry.message, attachments: entry.attachments });
53
55
  rejectionIdx++;
54
56
  }
55
57
  }
@@ -79,17 +81,18 @@ function RunGroup({ group, groupIdx, multiRun, maxDurationMs, rejectionMessages,
79
81
  return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [multiRun ? (_jsxs("div", { className: "text-muted-foreground text-xs font-medium", children: ["Run #", groupIdx + 1] })) : null, group.timings.map((t) => {
80
82
  if (isLifecycleEvent(t.phase)) {
81
83
  const key = `${t.agentRunId}-${t.phase}-${t.startedAt}`;
82
- const message = t.phase === 'run:rejected' ? rejectionMessages.get(key) : undefined;
83
- return _jsx(LifecycleEventRow, { timing: t, message: message }, key);
84
+ const rejection = t.phase === 'run:rejected' ? rejectionMessages.get(key) : undefined;
85
+ return (_jsx(LifecycleEventRow, { timing: t, message: rejection?.message, attachments: rejection?.attachments }, key));
84
86
  }
85
87
  return (_jsx(NodeTimingRow, { timing: t, maxDurationMs: maxDurationMs }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
86
88
  })] }));
87
89
  }
88
- function LifecycleEventRow({ timing, message }) {
90
+ function LifecycleEventRow({ timing, message, attachments, }) {
89
91
  const event = LIFECYCLE_EVENTS[timing.phase];
90
92
  if (!event)
91
93
  return null;
92
- return (_jsxs("div", { className: `text-xs ${event.colorClass}`, children: [_jsx("span", { children: event.label }), message ? (_jsxs("span", { "data-testid": "rejection-feedback-text", className: "text-muted-foreground ml-2 font-normal italic", children: ["\u2014 ", message] })) : null] }));
94
+ const hasAttachments = attachments && attachments.length > 0;
95
+ return (_jsxs("div", { className: `text-xs ${event.colorClass}`, children: [_jsx("span", { children: event.label }), message ? (_jsxs("span", { "data-testid": "rejection-feedback-text", className: "text-muted-foreground ml-2 font-normal italic", children: ["\u2014 ", message] })) : null, hasAttachments ? (_jsx("div", { "data-testid": "rejection-feedback-attachments", className: "mt-1.5 ml-2", children: _jsx(InlineAttachments, { text: "", attachmentPaths: attachments }) })) : null] }));
93
96
  }
94
97
  function NodeTimingRow({ timing, maxDurationMs, }) {
95
98
  const label = timing.phase.includes(':')
@@ -1 +1 @@
1
- {"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
1
+ {"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { ExternalLink, GitBranch, GitCommitHorizontal, Zap } from 'lucide-react';
4
+ import { InlineAttachments } from '../../common/inline-attachments/index.js';
4
5
  import { PrStatus } from '../../../../../../packages/core/src/domain/generated/output.js';
5
6
  import { cn } from '../../../lib/utils.js';
6
7
  import { Badge } from '../../ui/badge.js';
@@ -50,7 +51,7 @@ function FeatureInfo({ data }) {
50
51
  Boolean(data.createdAt);
51
52
  if (!hasInfo)
52
53
  return null;
53
- return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-info", className: "flex flex-col gap-3 p-4", children: [data.branch ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Branch" }), _jsxs("span", { className: "flex items-center gap-1.5 text-sm", children: [_jsx(GitBranch, { className: "text-muted-foreground h-3.5 w-3.5 shrink-0" }), _jsx("code", { className: "bg-muted rounded px-1 py-0.5 font-mono text-xs", children: data.branch }), data.baseBranch ? (_jsxs("span", { className: "text-muted-foreground text-xs", children: ["from ", data.baseBranch] })) : null] })] })) : null, data.oneLiner ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "One-Liner" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.oneLiner })] })) : null, data.userQuery ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "User Query" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.userQuery })] })) : null, data.summary ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Summary" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.summary })] })) : null, data.createdAt ? (_jsx(DetailRow, { label: "Created", value: formatRelativeTime(data.createdAt) })) : null] })] }));
54
+ return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-info", className: "flex flex-col gap-3 p-4", children: [data.branch ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Branch" }), _jsxs("span", { className: "flex items-center gap-1.5 text-sm", children: [_jsx(GitBranch, { className: "text-muted-foreground h-3.5 w-3.5 shrink-0" }), _jsx("code", { className: "bg-muted rounded px-1 py-0.5 font-mono text-xs", children: data.branch }), data.baseBranch ? (_jsxs("span", { className: "text-muted-foreground text-xs", children: ["from ", data.baseBranch] })) : null] })] })) : null, data.oneLiner ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "One-Liner" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.oneLiner })] })) : null, data.userQuery ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "User Query" }), _jsx(InlineAttachments, { text: data.userQuery })] })) : null, data.summary ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Summary" }), _jsx("span", { className: "text-sm leading-relaxed", children: data.summary })] })) : null, data.createdAt ? (_jsx(DetailRow, { label: "Created", value: formatRelativeTime(data.createdAt) })) : null] })] }));
54
55
  }
55
56
  // ── PR Info section ──────────────────────────────────────────────────
56
57
  const prStatusStyles = {
@@ -0,0 +1,3 @@
1
+ export { InlineAttachments, parseAttachmentRefs } from './inline-attachments.js';
2
+ export type { InlineAttachmentsProps } from './inline-attachments.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/inline-attachments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1 @@
1
+ export { InlineAttachments, parseAttachmentRefs } from './inline-attachments.js';
@@ -0,0 +1,20 @@
1
+ export interface InlineAttachmentsProps {
2
+ /** Text that may contain `@/path/to/file` attachment references. */
3
+ text: string;
4
+ /** Additional attachment paths to render (e.g. from rejection feedback). */
5
+ attachmentPaths?: string[];
6
+ }
7
+ interface TextSegment {
8
+ type: 'text';
9
+ value: string;
10
+ }
11
+ interface AttachmentSegment {
12
+ type: 'attachment';
13
+ path: string;
14
+ }
15
+ type Segment = TextSegment | AttachmentSegment;
16
+ /** Parse text into segments of plain text and attachment references. */
17
+ export declare function parseAttachmentRefs(text: string): Segment[];
18
+ export declare function InlineAttachments({ text, attachmentPaths }: InlineAttachmentsProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};
20
+ //# sourceMappingURL=inline-attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-attachments.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/inline-attachments/inline-attachments.tsx"],"names":[],"mappings":"AA6BA,MAAM,WAAW,sBAAsB;IACrC,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,KAAK,OAAO,GAAG,WAAW,GAAG,iBAAiB,CAAC;AAE/C,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAwB3D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,sBAAsB,2CA4BlF"}
@@ -0,0 +1,72 @@
1
+ /* eslint-disable @next/next/no-img-element -- Local file preview requires raw <img>, not next/image */
2
+ 'use client';
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ import { useState } from 'react';
5
+ import { Dialog, DialogTrigger, DialogContent, DialogTitle } from '../../ui/dialog.js';
6
+ import { VisuallyHidden } from 'radix-ui';
7
+ import { DownloadIcon, ImageOff } from 'lucide-react';
8
+ const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.bmp']);
9
+ function isImagePath(path) {
10
+ const dot = path.lastIndexOf('.');
11
+ return dot >= 0 && IMAGE_EXTS.has(path.slice(dot).toLowerCase());
12
+ }
13
+ function previewUrl(path) {
14
+ const ext = path.lastIndexOf('.') >= 0 ? path.slice(path.lastIndexOf('.')) : '';
15
+ const mimeGuess = isImagePath(path) ? `image/${ext.slice(1).replace('jpg', 'jpeg')}` : '';
16
+ const params = new URLSearchParams({ path, ...(mimeGuess && { mimeType: mimeGuess }) });
17
+ return `/api/attachments/preview?${params.toString()}`;
18
+ }
19
+ function getFilename(path) {
20
+ return path.split('/').pop() ?? path;
21
+ }
22
+ /** Regex matching `@/absolute/path` attachment references in text. */
23
+ const ATTACHMENT_REF_RE = /(?:^|\s)@(\/[^\s]+)/g;
24
+ /** Parse text into segments of plain text and attachment references. */
25
+ export function parseAttachmentRefs(text) {
26
+ const segments = [];
27
+ let lastIndex = 0;
28
+ for (const match of text.matchAll(ATTACHMENT_REF_RE)) {
29
+ const fullMatchStart = match.index;
30
+ // The path capture starts after the optional leading whitespace
31
+ const leadingWs = match[0].length - match[0].trimStart().length;
32
+ const refStart = fullMatchStart + leadingWs;
33
+ // Add text before this reference (including leading whitespace)
34
+ if (refStart > lastIndex) {
35
+ segments.push({ type: 'text', value: text.slice(lastIndex, refStart) });
36
+ }
37
+ segments.push({ type: 'attachment', path: match[1] });
38
+ lastIndex = fullMatchStart + match[0].length;
39
+ }
40
+ if (lastIndex < text.length) {
41
+ segments.push({ type: 'text', value: text.slice(lastIndex) });
42
+ }
43
+ return segments;
44
+ }
45
+ export function InlineAttachments({ text, attachmentPaths }) {
46
+ const segments = parseAttachmentRefs(text);
47
+ const hasInlineAttachments = segments.some((s) => s.type === 'attachment');
48
+ const extraPaths = attachmentPaths?.filter(Boolean) ?? [];
49
+ if (!hasInlineAttachments && extraPaths.length === 0) {
50
+ return _jsx("span", { className: "text-sm leading-relaxed", children: text });
51
+ }
52
+ return (_jsxs("div", { className: "flex flex-col gap-2", children: [segments.map((segment) => {
53
+ if (segment.type === 'text') {
54
+ const trimmed = segment.value.trim();
55
+ if (!trimmed)
56
+ return null;
57
+ return (_jsx("span", { className: "text-sm leading-relaxed", children: trimmed }, `text-${trimmed.slice(0, 40)}`));
58
+ }
59
+ return _jsx(AttachmentPreview, { path: segment.path }, `att-${segment.path}`);
60
+ }), extraPaths.map((path) => (_jsx(AttachmentPreview, { path: path }, `extra-${path}`)))] }));
61
+ }
62
+ function AttachmentPreview({ path }) {
63
+ const filename = getFilename(path);
64
+ const [loadError, setLoadError] = useState(false);
65
+ if (isImagePath(path)) {
66
+ if (loadError) {
67
+ return (_jsxs("div", { "data-testid": "inline-attachment-image-error", className: "text-muted-foreground flex items-center gap-2 rounded-md border px-3 py-2 text-sm", children: [_jsx(ImageOff, { className: "h-4 w-4 shrink-0" }), _jsx("span", { className: "truncate", children: filename })] }));
68
+ }
69
+ return (_jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: _jsx("img", { src: previewUrl(path), alt: filename, "data-testid": "inline-attachment-image", onError: () => setLoadError(true), className: "max-h-48 max-w-full cursor-pointer rounded-md border object-contain transition-opacity hover:opacity-80" }) }), _jsxs(DialogContent, { className: "max-w-3xl gap-0 overflow-hidden border-0 p-0 [&>button:last-child]:!cursor-pointer [&>button:last-child]:!rounded-full [&>button:last-child]:!bg-black/70 [&>button:last-child]:!p-1.5 [&>button:last-child]:!text-white [&>button:last-child]:!opacity-100 [&>button:last-child]:!shadow-lg [&>button:last-child]:!backdrop-blur-md [&>button:last-child]:hover:!bg-black/90", children: [_jsx(VisuallyHidden.Root, { children: _jsxs(DialogTitle, { children: ["Preview: ", filename] }) }), _jsx("div", { className: "relative bg-black/90", children: _jsx("img", { src: previewUrl(path), alt: filename, className: "h-auto max-h-[70vh] w-full object-contain" }) }), _jsxs("div", { className: "bg-background flex items-center gap-3 px-4 py-3", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: _jsx("span", { className: "truncate text-sm font-medium", children: filename }) }), _jsx("a", { href: previewUrl(path), download: filename, className: "text-muted-foreground hover:text-foreground shrink-0 cursor-pointer rounded p-1.5 transition-colors", "aria-label": `Download ${filename}`, children: _jsx(DownloadIcon, { className: "h-4 w-4" }) })] })] })] }));
70
+ }
71
+ return (_jsx("a", { href: previewUrl(path), download: filename, "data-testid": "inline-attachment-file", className: "text-primary text-sm underline underline-offset-2", children: filename }));
72
+ }
@@ -0,0 +1,16 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { InlineAttachments } from './inline-attachments.js';
3
+ declare const meta: Meta<typeof InlineAttachments>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof InlineAttachments>;
6
+ /** Plain text with no attachment references. */
7
+ export declare const PlainText: Story;
8
+ /** Text with an image attachment reference. */
9
+ export declare const WithImageAttachment: Story;
10
+ /** Text with multiple attachment references. */
11
+ export declare const MultipleAttachments: Story;
12
+ /** Text with a non-image file attachment. */
13
+ export declare const FileAttachment: Story;
14
+ /** Extra attachment paths passed via prop (e.g. rejection feedback). */
15
+ export declare const ExtraAttachmentPaths: Story;
16
+ //# sourceMappingURL=inline-attachments.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-attachments.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/inline-attachments/inline-attachments.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,iBAAiB,CAYxC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEhD,gDAAgD;AAChD,eAAO,MAAM,SAAS,EAAE,KAIvB,CAAC;AAEF,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB,EAAE,KAIjC,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,mBAAmB,EAAE,KAIjC,CAAC;AAEF,6CAA6C;AAC7C,eAAO,MAAM,cAAc,EAAE,KAI5B,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,EAAE,KAQlC,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { InlineAttachments } from './inline-attachments.js';
3
+ const meta = {
4
+ title: 'Common/InlineAttachments',
5
+ component: InlineAttachments,
6
+ tags: ['autodocs'],
7
+ parameters: { layout: 'padded' },
8
+ decorators: [
9
+ (Story) => (_jsx("div", { style: { maxWidth: '400px', border: '1px solid var(--color-border)', padding: '16px' }, children: _jsx(Story, {}) })),
10
+ ],
11
+ };
12
+ export default meta;
13
+ /** Plain text with no attachment references. */
14
+ export const PlainText = {
15
+ args: {
16
+ text: 'Fix the login bug where users cannot sign in with Google OAuth.',
17
+ },
18
+ };
19
+ /** Text with an image attachment reference. */
20
+ export const WithImageAttachment = {
21
+ args: {
22
+ text: 'The button is misaligned, see screenshot:\n@/home/user/.shep/attachments/pending-abc/screenshot.png',
23
+ },
24
+ };
25
+ /** Text with multiple attachment references. */
26
+ export const MultipleAttachments = {
27
+ args: {
28
+ text: 'Before: @/home/user/.shep/attachments/pending-abc/before.png\nAfter: @/home/user/.shep/attachments/pending-abc/after.png',
29
+ },
30
+ };
31
+ /** Text with a non-image file attachment. */
32
+ export const FileAttachment = {
33
+ args: {
34
+ text: 'See the log file:\n@/home/user/.shep/attachments/pending-abc/error.log',
35
+ },
36
+ };
37
+ /** Extra attachment paths passed via prop (e.g. rejection feedback). */
38
+ export const ExtraAttachmentPaths = {
39
+ args: {
40
+ text: 'Please fix the layout issues',
41
+ attachmentPaths: [
42
+ '/home/user/.shep/attachments/pending-abc/screenshot1.png',
43
+ '/home/user/.shep/attachments/pending-abc/screenshot2.png',
44
+ ],
45
+ },
46
+ };