auramaxx 0.0.1

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 (418) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +77 -0
  3. package/apps/desktop-electron/main.js +428 -0
  4. package/bin/auramaxx.js +1063 -0
  5. package/docs/ADAPTERS.md +466 -0
  6. package/docs/AGENT_SETUP.md +159 -0
  7. package/docs/API.md +127 -0
  8. package/docs/APPS.md +199 -0
  9. package/docs/ARCHITECTURE.md +235 -0
  10. package/docs/AUTH.md +318 -0
  11. package/docs/BEST-PRACTICES.md +82 -0
  12. package/docs/CLI.md +141 -0
  13. package/docs/DESKTOP_ELECTRON.md +26 -0
  14. package/docs/DEVELOPING-APPS.md +453 -0
  15. package/docs/MCP.md +122 -0
  16. package/docs/PACKAGING_POLICY.md +19 -0
  17. package/docs/PERMISSION.md +137 -0
  18. package/docs/PROTOCOL.md +142 -0
  19. package/docs/README.md +50 -0
  20. package/docs/SKILLS.md +132 -0
  21. package/docs/TROUBLESHOOTING.md +376 -0
  22. package/docs/WORKSPACE.md +673 -0
  23. package/docs/agent-auth.md +14 -0
  24. package/docs/api/authentication.md +79 -0
  25. package/docs/api/secrets/api-keys.md +28 -0
  26. package/docs/api/secrets/credentials.md +80 -0
  27. package/docs/api/secrets/sharing.md +48 -0
  28. package/docs/api/system.md +41 -0
  29. package/docs/api/wallets/apps-strategies.md +66 -0
  30. package/docs/api/wallets/core.md +46 -0
  31. package/docs/api/wallets/data-portfolio.md +42 -0
  32. package/docs/aura-file.md +48 -0
  33. package/docs/core-concepts/FEATURES.md +114 -0
  34. package/docs/credentials.md +120 -0
  35. package/docs/external/HOW_TO_AURAMAXX/GETTING_SECRETS.md +33 -0
  36. package/docs/external/HOW_TO_AURAMAXX/README.md +45 -0
  37. package/docs/external/getting-started.md +10 -0
  38. package/docs/external/overview.md +19 -0
  39. package/docs/external/persona-paths.md +7 -0
  40. package/docs/external/share-secret.md +76 -0
  41. package/docs/external/why-aura.md +7 -0
  42. package/docs/security.md +227 -0
  43. package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
  44. package/docs/wallet/AI.md +508 -0
  45. package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
  46. package/docs/wallet/README.md +47 -0
  47. package/docs/wallet/STRATEGY.md +89 -0
  48. package/next.config.ts +28 -0
  49. package/package.json +167 -0
  50. package/postcss.config.mjs +8 -0
  51. package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
  52. package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
  53. package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
  54. package/prisma/migrations/20260222090000_update_admin_ttl_default/migration.sql +10 -0
  55. package/prisma/migrations/migration_lock.toml +3 -0
  56. package/prisma/schema.prisma +447 -0
  57. package/public/logo.webp +0 -0
  58. package/scripts/add-app.js +245 -0
  59. package/server/abi/SwapHelper.json +438 -0
  60. package/server/cli/approval.ts +447 -0
  61. package/server/cli/commands/actions.ts +474 -0
  62. package/server/cli/commands/api.ts +220 -0
  63. package/server/cli/commands/apikey.ts +277 -0
  64. package/server/cli/commands/app.ts +204 -0
  65. package/server/cli/commands/auth.ts +464 -0
  66. package/server/cli/commands/cron.ts +24 -0
  67. package/server/cli/commands/diary.ts +274 -0
  68. package/server/cli/commands/doctor.ts +1247 -0
  69. package/server/cli/commands/env.ts +476 -0
  70. package/server/cli/commands/experimental.ts +69 -0
  71. package/server/cli/commands/init.ts +798 -0
  72. package/server/cli/commands/lock.ts +157 -0
  73. package/server/cli/commands/mcp.ts +285 -0
  74. package/server/cli/commands/quickhack.ts +86 -0
  75. package/server/cli/commands/release-check.ts +231 -0
  76. package/server/cli/commands/restore.ts +314 -0
  77. package/server/cli/commands/service.ts +320 -0
  78. package/server/cli/commands/shell-hook.ts +512 -0
  79. package/server/cli/commands/skill.ts +216 -0
  80. package/server/cli/commands/start.ts +139 -0
  81. package/server/cli/commands/status.ts +59 -0
  82. package/server/cli/commands/stop.ts +36 -0
  83. package/server/cli/commands/token.ts +180 -0
  84. package/server/cli/commands/unlock.ts +50 -0
  85. package/server/cli/commands/vault.ts +1323 -0
  86. package/server/cli/commands/wallet.ts +209 -0
  87. package/server/cli/index.ts +280 -0
  88. package/server/cli/lib/approval-poll.ts +94 -0
  89. package/server/cli/lib/aura-parser.ts +64 -0
  90. package/server/cli/lib/credential-create.ts +74 -0
  91. package/server/cli/lib/credential-resolve.ts +280 -0
  92. package/server/cli/lib/dotenv-migrate.ts +116 -0
  93. package/server/cli/lib/dotenv-parser.ts +146 -0
  94. package/server/cli/lib/escalation.ts +57 -0
  95. package/server/cli/lib/http.ts +91 -0
  96. package/server/cli/lib/init-steps.ts +76 -0
  97. package/server/cli/lib/local-agent-trust.ts +45 -0
  98. package/server/cli/lib/lock-unlock-helper.ts +71 -0
  99. package/server/cli/lib/process.ts +162 -0
  100. package/server/cli/lib/prompt.ts +294 -0
  101. package/server/cli/lib/theme.ts +240 -0
  102. package/server/cli/socket.ts +579 -0
  103. package/server/cli/transport-client.ts +50 -0
  104. package/server/cron/index.ts +137 -0
  105. package/server/cron/job.ts +31 -0
  106. package/server/cron/jobs/balance-sync.ts +436 -0
  107. package/server/cron/jobs/incoming-scan.ts +506 -0
  108. package/server/cron/jobs/native-price.ts +70 -0
  109. package/server/cron/jobs/orphan-cleanup.ts +40 -0
  110. package/server/cron/jobs/strategy-runner.ts +175 -0
  111. package/server/cron/scheduler.ts +125 -0
  112. package/server/index.ts +420 -0
  113. package/server/lib/adapters/factory.ts +119 -0
  114. package/server/lib/adapters/index.ts +19 -0
  115. package/server/lib/adapters/router.ts +297 -0
  116. package/server/lib/adapters/telegram.ts +645 -0
  117. package/server/lib/adapters/types.ts +89 -0
  118. package/server/lib/adapters/webhook.ts +95 -0
  119. package/server/lib/address.ts +49 -0
  120. package/server/lib/agent-auth/contracts.ts +1194 -0
  121. package/server/lib/agent-profiles.ts +419 -0
  122. package/server/lib/ai.ts +285 -0
  123. package/server/lib/api-registry/contracts.ts +86 -0
  124. package/server/lib/api-registry/validation.ts +172 -0
  125. package/server/lib/apikey-migration.ts +258 -0
  126. package/server/lib/app-installer.ts +505 -0
  127. package/server/lib/app-tokens.ts +247 -0
  128. package/server/lib/approval-link.ts +27 -0
  129. package/server/lib/auth.ts +314 -0
  130. package/server/lib/auto-execute.ts +160 -0
  131. package/server/lib/batch.ts +242 -0
  132. package/server/lib/cold.ts +1048 -0
  133. package/server/lib/config.ts +408 -0
  134. package/server/lib/credential-access-audit.ts +85 -0
  135. package/server/lib/credential-access-policy.ts +111 -0
  136. package/server/lib/credential-health.ts +343 -0
  137. package/server/lib/credential-import.ts +608 -0
  138. package/server/lib/credential-scope.ts +102 -0
  139. package/server/lib/credential-shares.ts +190 -0
  140. package/server/lib/credential-transport.ts +533 -0
  141. package/server/lib/credential-vault.ts +77 -0
  142. package/server/lib/credentials.ts +422 -0
  143. package/server/lib/crypto.ts +8 -0
  144. package/server/lib/db.ts +58 -0
  145. package/server/lib/defaults.ts +386 -0
  146. package/server/lib/dex/index.ts +80 -0
  147. package/server/lib/dex/relay.ts +235 -0
  148. package/server/lib/dex/types.ts +59 -0
  149. package/server/lib/dex/uniswap.ts +370 -0
  150. package/server/lib/diary.ts +34 -0
  151. package/server/lib/dont-ask-again-policy.ts +41 -0
  152. package/server/lib/e2e-agent/artifacts.ts +36 -0
  153. package/server/lib/e2e-agent/contracts.ts +112 -0
  154. package/server/lib/e2e-agent/validation.ts +135 -0
  155. package/server/lib/encrypt.ts +114 -0
  156. package/server/lib/error.ts +20 -0
  157. package/server/lib/events.ts +217 -0
  158. package/server/lib/feature-flags.ts +93 -0
  159. package/server/lib/hot.ts +357 -0
  160. package/server/lib/human-action-summary.ts +80 -0
  161. package/server/lib/key-fingerprint.ts +28 -0
  162. package/server/lib/logger.ts +340 -0
  163. package/server/lib/network.ts +137 -0
  164. package/server/lib/notifications.ts +230 -0
  165. package/server/lib/oauth2-refresh.ts +241 -0
  166. package/server/lib/oursecret.ts +71 -0
  167. package/server/lib/passkey-credential.ts +360 -0
  168. package/server/lib/passkey.ts +68 -0
  169. package/server/lib/permissions.ts +299 -0
  170. package/server/lib/pino.ts +24 -0
  171. package/server/lib/policy-preview.ts +138 -0
  172. package/server/lib/price.ts +338 -0
  173. package/server/lib/prices.ts +34 -0
  174. package/server/lib/project-scope.ts +297 -0
  175. package/server/lib/resolve-action.ts +328 -0
  176. package/server/lib/resolve.ts +36 -0
  177. package/server/lib/secret-gist-share.ts +296 -0
  178. package/server/lib/sessions.ts +634 -0
  179. package/server/lib/socket-path.ts +56 -0
  180. package/server/lib/solana/connection.ts +26 -0
  181. package/server/lib/solana/jupiter.ts +128 -0
  182. package/server/lib/solana/transfer.ts +108 -0
  183. package/server/lib/solana/wallet.ts +136 -0
  184. package/server/lib/strategy/emits.ts +21 -0
  185. package/server/lib/strategy/engine.ts +1305 -0
  186. package/server/lib/strategy/executor.ts +115 -0
  187. package/server/lib/strategy/hook-context.ts +159 -0
  188. package/server/lib/strategy/hooks.ts +990 -0
  189. package/server/lib/strategy/index.ts +28 -0
  190. package/server/lib/strategy/installer.ts +305 -0
  191. package/server/lib/strategy/loader.ts +256 -0
  192. package/server/lib/strategy/message.ts +237 -0
  193. package/server/lib/strategy/repository.ts +218 -0
  194. package/server/lib/strategy/session-logger.ts +693 -0
  195. package/server/lib/strategy/sources.ts +288 -0
  196. package/server/lib/strategy/state.ts +189 -0
  197. package/server/lib/strategy/templates.ts +403 -0
  198. package/server/lib/strategy/tick.ts +404 -0
  199. package/server/lib/strategy/types.ts +230 -0
  200. package/server/lib/swap.ts +3 -0
  201. package/server/lib/temp.ts +86 -0
  202. package/server/lib/token-metadata.ts +86 -0
  203. package/server/lib/token-safety.ts +200 -0
  204. package/server/lib/token-search.ts +444 -0
  205. package/server/lib/totp.ts +194 -0
  206. package/server/lib/transactions.ts +123 -0
  207. package/server/lib/transport.ts +84 -0
  208. package/server/lib/txhistory/decoder.ts +262 -0
  209. package/server/lib/txhistory/enricher.ts +652 -0
  210. package/server/lib/txhistory/index.ts +391 -0
  211. package/server/lib/txhistory/signatures.ts +59 -0
  212. package/server/lib/update-check.ts +35 -0
  213. package/server/lib/verified-summary.ts +414 -0
  214. package/server/lib/view-registry.ts +80 -0
  215. package/server/mcp/profile-policy.ts +30 -0
  216. package/server/mcp/server.ts +1589 -0
  217. package/server/mcp/tools.ts +276 -0
  218. package/server/middleware/auth.ts +119 -0
  219. package/server/middleware/requestLogger.ts +84 -0
  220. package/server/routes/actions.ts +539 -0
  221. package/server/routes/adapters.ts +711 -0
  222. package/server/routes/addressbook.ts +113 -0
  223. package/server/routes/ai.ts +34 -0
  224. package/server/routes/apikeys.ts +343 -0
  225. package/server/routes/apps.ts +601 -0
  226. package/server/routes/auth.ts +406 -0
  227. package/server/routes/backup.ts +404 -0
  228. package/server/routes/batch.ts +270 -0
  229. package/server/routes/bookmarks.ts +162 -0
  230. package/server/routes/credential-shares.ts +380 -0
  231. package/server/routes/credential-vaults.ts +159 -0
  232. package/server/routes/credentials.ts +1782 -0
  233. package/server/routes/dashboard.ts +97 -0
  234. package/server/routes/defaults.ts +124 -0
  235. package/server/routes/flags.ts +11 -0
  236. package/server/routes/fund.ts +225 -0
  237. package/server/routes/heartbeat.ts +375 -0
  238. package/server/routes/import.ts +364 -0
  239. package/server/routes/launch.ts +665 -0
  240. package/server/routes/lock.ts +54 -0
  241. package/server/routes/logs.ts +68 -0
  242. package/server/routes/nuke.ts +111 -0
  243. package/server/routes/passkey-credentials.ts +99 -0
  244. package/server/routes/passkey.ts +366 -0
  245. package/server/routes/portfolio.ts +217 -0
  246. package/server/routes/price.ts +63 -0
  247. package/server/routes/resolve.ts +31 -0
  248. package/server/routes/security.ts +45 -0
  249. package/server/routes/send-evm.ts +241 -0
  250. package/server/routes/send-solana.ts +281 -0
  251. package/server/routes/send.ts +178 -0
  252. package/server/routes/setup.ts +210 -0
  253. package/server/routes/strategy.ts +894 -0
  254. package/server/routes/swap-evm.ts +352 -0
  255. package/server/routes/swap-solana.ts +176 -0
  256. package/server/routes/swap.ts +356 -0
  257. package/server/routes/token.ts +247 -0
  258. package/server/routes/unlock.ts +467 -0
  259. package/server/routes/views.ts +41 -0
  260. package/server/routes/wallet-assets.ts +361 -0
  261. package/server/routes/wallet-transactions.ts +515 -0
  262. package/server/routes/wallet.ts +709 -0
  263. package/server/types.ts +146 -0
  264. package/shared/credential-field-schema.ts +248 -0
  265. package/skills/auramaxx/HEARTBEAT.md +78 -0
  266. package/skills/auramaxx/SKILL.md +745 -0
  267. package/skills/auramaxx/docs/AGENT_SETUP.md +155 -0
  268. package/skills/auramaxx/docs/API.md +127 -0
  269. package/skills/auramaxx/docs/AUTH.md +318 -0
  270. package/skills/auramaxx/docs/CLI.md +130 -0
  271. package/skills/auramaxx/docs/MCP.md +122 -0
  272. package/skills/auramaxx/docs/TROUBLESHOOTING.md +357 -0
  273. package/skills/auramaxx/docs/WORKSPACE.md +673 -0
  274. package/skills/auramaxx/docs/security.md +227 -0
  275. package/skills/task-lifecycle/SKILL.md +378 -0
  276. package/src/app/api/[...doc]/page.tsx +36 -0
  277. package/src/app/api/agent-requests/route.ts +30 -0
  278. package/src/app/api/apps/install/route.ts +132 -0
  279. package/src/app/api/apps/manifests/route.ts +16 -0
  280. package/src/app/api/apps/static/[...path]/route.ts +57 -0
  281. package/src/app/api/docs/plain/route.ts +74 -0
  282. package/src/app/api/events/route.ts +92 -0
  283. package/src/app/api/page.tsx +290 -0
  284. package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
  285. package/src/app/api/workspace/[id]/apps/route.ts +81 -0
  286. package/src/app/api/workspace/[id]/export/route.ts +67 -0
  287. package/src/app/api/workspace/[id]/route.ts +168 -0
  288. package/src/app/api/workspace/auth.ts +40 -0
  289. package/src/app/api/workspace/config/route.ts +121 -0
  290. package/src/app/api/workspace/import/route.ts +127 -0
  291. package/src/app/api/workspace/route.ts +116 -0
  292. package/src/app/app-legacy-do-not-use/page.tsx +2245 -0
  293. package/src/app/apple-icon.png +0 -0
  294. package/src/app/approve/[actionId]/page.tsx +409 -0
  295. package/src/app/docs/DocsPageContent.tsx +269 -0
  296. package/src/app/docs/[...doc]/page.tsx +41 -0
  297. package/src/app/docs/page.tsx +38 -0
  298. package/src/app/favicon.ico +0 -0
  299. package/src/app/globals.css +819 -0
  300. package/src/app/health/page.tsx +5 -0
  301. package/src/app/hello/page.tsx +102 -0
  302. package/src/app/icon.png +0 -0
  303. package/src/app/layout.tsx +39 -0
  304. package/src/app/page.tsx +1964 -0
  305. package/src/app/privacy/page.tsx +63 -0
  306. package/src/app/providers.tsx +87 -0
  307. package/src/app/share/[token]/page.tsx +295 -0
  308. package/src/app/terms/page.tsx +80 -0
  309. package/src/components/ChainSelector.tsx +44 -0
  310. package/src/components/HumanActionBar.tsx +697 -0
  311. package/src/components/NotificationDrawer.tsx +387 -0
  312. package/src/components/PasskeyEnrollmentPrompt.tsx +235 -0
  313. package/src/components/apps/AgentKeysApp.tsx +490 -0
  314. package/src/components/apps/App.tsx +153 -0
  315. package/src/components/apps/AppGrid.tsx +15 -0
  316. package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
  317. package/src/components/apps/DraggableApp.tsx +562 -0
  318. package/src/components/apps/IFrameApp.tsx +73 -0
  319. package/src/components/apps/LogsApp.tsx +360 -0
  320. package/src/components/apps/SendApp.tsx +394 -0
  321. package/src/components/apps/SetupWizardApp.tsx +1004 -0
  322. package/src/components/apps/SystemDefaultsApp.tsx +845 -0
  323. package/src/components/apps/ThirdPartyApp.tsx +428 -0
  324. package/src/components/apps/TokenApp.tsx +319 -0
  325. package/src/components/apps/TransactionsApp.tsx +438 -0
  326. package/src/components/apps/WalletDetailApp.tsx +1505 -0
  327. package/src/components/apps/index.ts +13 -0
  328. package/src/components/design-system/Button.tsx +88 -0
  329. package/src/components/design-system/ChainIndicator.tsx +65 -0
  330. package/src/components/design-system/ChainSelector.tsx +147 -0
  331. package/src/components/design-system/ConfirmationModal.tsx +107 -0
  332. package/src/components/design-system/ConfirmationPopover.tsx +81 -0
  333. package/src/components/design-system/DownloadButton.tsx +149 -0
  334. package/src/components/design-system/Drawer.tsx +133 -0
  335. package/src/components/design-system/FilterDropdown.tsx +183 -0
  336. package/src/components/design-system/ItemPicker.tsx +157 -0
  337. package/src/components/design-system/Modal.tsx +296 -0
  338. package/src/components/design-system/Popover.tsx +142 -0
  339. package/src/components/design-system/TextInput.tsx +85 -0
  340. package/src/components/design-system/Toggle.tsx +65 -0
  341. package/src/components/design-system/TyvekCollapsibleSection.tsx +55 -0
  342. package/src/components/design-system/index.ts +14 -0
  343. package/src/components/docs/ClientSideMarkdown.tsx +51 -0
  344. package/src/components/docs/DocsSearchBar.tsx +118 -0
  345. package/src/components/docs/DocsThemeToggle.tsx +38 -0
  346. package/src/components/docs/PersistentDocGroup.tsx +91 -0
  347. package/src/components/docs/ShareUrlButton.tsx +33 -0
  348. package/src/components/docs/SidebarScrollMemory.tsx +56 -0
  349. package/src/components/health/CredentialHealthDashboard.tsx +214 -0
  350. package/src/components/icons/ChainIcons.tsx +72 -0
  351. package/src/components/layout/AppStoreDrawer.tsx +369 -0
  352. package/src/components/layout/ContentArea.tsx +21 -0
  353. package/src/components/layout/CreateViewModal.tsx +88 -0
  354. package/src/components/layout/LeftRail.tsx +114 -0
  355. package/src/components/layout/TabBar.tsx +284 -0
  356. package/src/components/layout/WalletSidebar.tsx +1030 -0
  357. package/src/components/layout/index.ts +6 -0
  358. package/src/components/marketing/AuraMaxxSpecOverlay.tsx +653 -0
  359. package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
  360. package/src/components/vault/ApiKeysConsole.tsx +1272 -0
  361. package/src/components/vault/AuditConsole.tsx +600 -0
  362. package/src/components/vault/CredentialDetail.tsx +625 -0
  363. package/src/components/vault/CredentialEmpty.tsx +55 -0
  364. package/src/components/vault/CredentialField.tsx +583 -0
  365. package/src/components/vault/CredentialForm.tsx +1484 -0
  366. package/src/components/vault/CredentialList.tsx +265 -0
  367. package/src/components/vault/CredentialRow.tsx +130 -0
  368. package/src/components/vault/CredentialShareModal.tsx +273 -0
  369. package/src/components/vault/CredentialVault.tsx +1662 -0
  370. package/src/components/vault/CredentialWalletWidget.tsx +103 -0
  371. package/src/components/vault/DocsConsole.tsx +113 -0
  372. package/src/components/vault/ImportCredentialsModal.tsx +578 -0
  373. package/src/components/vault/LargeTypeModal.tsx +88 -0
  374. package/src/components/vault/PasswordGenerator.tsx +232 -0
  375. package/src/components/vault/TOTPDisplay.tsx +108 -0
  376. package/src/components/vault/TotpSetupPanel.tsx +198 -0
  377. package/src/components/vault/VaultSidebar.tsx +881 -0
  378. package/src/components/vault/credentialFormName.ts +91 -0
  379. package/src/components/vault/hooks/useVaultKeyboardShortcuts.ts +69 -0
  380. package/src/components/vault/types.ts +56 -0
  381. package/src/context/AuthContext.tsx +365 -0
  382. package/src/context/PriceContext.tsx +113 -0
  383. package/src/context/ThemeContext.tsx +164 -0
  384. package/src/context/WebSocketContext.tsx +269 -0
  385. package/src/context/WorkspaceContext.tsx +668 -0
  386. package/src/hooks/index.ts +4 -0
  387. package/src/hooks/useAgentActions.ts +552 -0
  388. package/src/hooks/useBalance.ts +103 -0
  389. package/src/hooks/useBalances.ts +129 -0
  390. package/src/hooks/useTheme.ts +156 -0
  391. package/src/instrumentation.ts +12 -0
  392. package/src/lib/api-docs.ts +154 -0
  393. package/src/lib/api.ts +474 -0
  394. package/src/lib/app-loader.ts +148 -0
  395. package/src/lib/app-registry.ts +178 -0
  396. package/src/lib/app-sdk.ts +157 -0
  397. package/src/lib/audit-console-adapter.ts +151 -0
  398. package/src/lib/auth-client.ts +75 -0
  399. package/src/lib/config.ts +74 -0
  400. package/src/lib/credential-field-schema.ts +11 -0
  401. package/src/lib/crypto.ts +112 -0
  402. package/src/lib/db.ts +21 -0
  403. package/src/lib/docs.ts +544 -0
  404. package/src/lib/events.ts +363 -0
  405. package/src/lib/pino.ts +24 -0
  406. package/src/lib/theme-handlers.ts +168 -0
  407. package/src/lib/theme.ts +351 -0
  408. package/src/lib/tokenData.ts +378 -0
  409. package/src/lib/totp-import.ts +57 -0
  410. package/src/lib/vault-crypto.ts +129 -0
  411. package/src/lib/view-registry.ts +57 -0
  412. package/src/lib/websocket-server.ts +302 -0
  413. package/src/lib/websocket-setup.ts +79 -0
  414. package/src/lib/wordlist.ts +2050 -0
  415. package/src/lib/workspace-handlers.ts +285 -0
  416. package/start.sh +170 -0
  417. package/tailwind.config.ts +99 -0
  418. package/tsconfig.json +42 -0
@@ -0,0 +1,583 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useRef, useCallback, useEffect, useMemo } from 'react';
4
+ import { ChevronDown, Copy, Eye, EyeOff, Maximize2, Loader2 } from 'lucide-react';
5
+ import { Popover } from '@/components/design-system';
6
+ import { decryptCredentialPayload } from '@/lib/vault-crypto';
7
+ import { api, Api } from '@/lib/api';
8
+ import { canonicalizeCredentialFieldKey, NOTE_CONTENT_KEY } from '@/lib/credential-field-schema';
9
+ import { marked } from 'marked';
10
+
11
+ type SensitiveInteractionMode = 'default' | 'hover-copy' | 'markdown-hover-copy';
12
+
13
+ interface CredentialFieldProps {
14
+ label: string;
15
+ value?: string;
16
+ copyValue?: string;
17
+ trailingValue?: React.ReactNode;
18
+ credentialId: string;
19
+ fieldKey: string;
20
+ isSensitive: boolean;
21
+ onShowLargeType: (value: string) => void;
22
+ sensitiveInteractionMode?: SensitiveInteractionMode;
23
+ actionsPosition?: 'left' | 'right';
24
+ showActionsInHoverCopyMode?: boolean;
25
+ sensitiveClickBehavior?: 'reveal' | 'copy';
26
+ disableLargeType?: boolean;
27
+ renderMarkdown?: boolean;
28
+ }
29
+
30
+ export const CredentialField: React.FC<CredentialFieldProps> = ({
31
+ label,
32
+ value,
33
+ copyValue,
34
+ trailingValue,
35
+ credentialId,
36
+ fieldKey,
37
+ isSensitive,
38
+ onShowLargeType,
39
+ sensitiveInteractionMode = 'default',
40
+ actionsPosition = 'left',
41
+ showActionsInHoverCopyMode = false,
42
+ sensitiveClickBehavior = 'reveal',
43
+ disableLargeType = false,
44
+ renderMarkdown = false,
45
+ }) => {
46
+ const [copied, setCopied] = useState(false);
47
+ const [revealed, setRevealed] = useState(false);
48
+ const [revealedValue, setRevealedValue] = useState<string | null>(null);
49
+ const [decrypting, setDecrypting] = useState(false);
50
+ const [error, setError] = useState<string | null>(null);
51
+ const [menuOpen, setMenuOpen] = useState(false);
52
+ const menuAnchorRef = useRef<HTMLButtonElement>(null);
53
+ const revealTimerRef = useRef<ReturnType<typeof setTimeout>>(null);
54
+ const copyTimerRef = useRef<ReturnType<typeof setTimeout>>(null);
55
+ const isHoverCopyMode = sensitiveInteractionMode === 'hover-copy' || sensitiveInteractionMode === 'markdown-hover-copy';
56
+ const isMarkdownMode = sensitiveInteractionMode === 'markdown-hover-copy';
57
+ const isRowHoverCopyMode = sensitiveInteractionMode === 'hover-copy';
58
+ const isSensitiveClickCopyMode = !isHoverCopyMode && sensitiveClickBehavior === 'copy';
59
+ const canShowSensitiveActions = !isHoverCopyMode || showActionsInHoverCopyMode;
60
+
61
+ const markdownHtml = useMemo(() => {
62
+ if (!isMarkdownMode || revealedValue == null) return '';
63
+ const escaped = revealedValue
64
+ .replace(/&/g, '&amp;')
65
+ .replace(/</g, '&lt;')
66
+ .replace(/>/g, '&gt;');
67
+ return marked.parse(escaped, { async: false, breaks: true }) as string;
68
+ }, [isMarkdownMode, revealedValue]);
69
+
70
+ const plainMarkdownHtml = useMemo(() => {
71
+ if (!renderMarkdown || !value) return '';
72
+ const escaped = value
73
+ .replace(/&/g, '&amp;')
74
+ .replace(/</g, '&lt;')
75
+ .replace(/>/g, '&gt;');
76
+ return marked.parse(escaped, { async: false, breaks: true }) as string;
77
+ }, [renderMarkdown, value]);
78
+
79
+ useEffect(() => {
80
+ return () => {
81
+ if (revealTimerRef.current) clearTimeout(revealTimerRef.current);
82
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
83
+ };
84
+ }, []);
85
+
86
+ useEffect(() => {
87
+ setCopied(false);
88
+ setRevealed(false);
89
+ setRevealedValue(null);
90
+ setError(null);
91
+ setMenuOpen(false);
92
+ if (revealTimerRef.current) clearTimeout(revealTimerRef.current);
93
+ }, [credentialId, fieldKey]);
94
+
95
+ // Auto-decrypt intentionally removed for markdown notes to avoid layout shift.
96
+ // User clicks to reveal first, then clicks again to copy.
97
+
98
+ const markCopied = useCallback(() => {
99
+ setCopied(true);
100
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
101
+ copyTimerRef.current = setTimeout(() => setCopied(false), 2000);
102
+ }, []);
103
+
104
+ const revealForWindow = useCallback((val: string) => {
105
+ setRevealedValue(val);
106
+ setRevealed(true);
107
+ if (revealTimerRef.current) clearTimeout(revealTimerRef.current);
108
+ revealTimerRef.current = setTimeout(() => {
109
+ setRevealed(false);
110
+ setRevealedValue(null);
111
+ }, 30000);
112
+ }, []);
113
+
114
+ // Silent decrypt — no spinner / no visual state changes.
115
+ // Use for copy and large-type where layout must stay stable.
116
+ const decryptFieldSilent = useCallback(async (): Promise<string | null> => {
117
+ if (revealedValue != null) return revealedValue;
118
+ try {
119
+ const res = await api.post<{ encrypted: string }>(Api.Wallet, `/credentials/${credentialId}/read`);
120
+ const plaintext = await decryptCredentialPayload(res.encrypted);
121
+ const parsed = JSON.parse(plaintext) as {
122
+ type?: string;
123
+ fields?: Array<{ key: string; value: string }>;
124
+ };
125
+ const credentialType = typeof parsed.type === 'string' ? parsed.type : '';
126
+ const canonicalTargetKey = canonicalizeCredentialFieldKey(credentialType, fieldKey);
127
+ const field = parsed.fields?.find((f) => (
128
+ canonicalizeCredentialFieldKey(credentialType, f.key) === canonicalTargetKey
129
+ || (fieldKey === NOTE_CONTENT_KEY && f.key === 'value')
130
+ ));
131
+ if (!field) return null;
132
+ return field.value;
133
+ } catch {
134
+ return null;
135
+ }
136
+ }, [credentialId, fieldKey, revealedValue]);
137
+
138
+ // Visual decrypt — shows spinner. Used for reveal where user expects feedback.
139
+ const decryptField = useCallback(async (): Promise<string | null> => {
140
+ if (revealedValue != null) return revealedValue;
141
+ setDecrypting(true);
142
+ setError(null);
143
+ try {
144
+ const val = await decryptFieldSilent();
145
+ if (val == null) {
146
+ setError('Field not found');
147
+ }
148
+ return val;
149
+ } catch {
150
+ setError('Decryption failed -- try re-unlocking');
151
+ return null;
152
+ } finally {
153
+ setDecrypting(false);
154
+ }
155
+ }, [decryptFieldSilent, revealedValue]);
156
+
157
+ const handleCopySensitive = useCallback(async () => {
158
+ if (decrypting) return;
159
+ setMenuOpen(false);
160
+ const val = await decryptFieldSilent();
161
+ if (val != null) {
162
+ navigator.clipboard.writeText(val);
163
+ markCopied();
164
+ }
165
+ }, [decryptFieldSilent, decrypting, markCopied]);
166
+
167
+ const handleReveal = useCallback(async () => {
168
+ if (decrypting || (revealed && revealedValue != null)) return;
169
+ setMenuOpen(false);
170
+ const val = await decryptField();
171
+ if (val != null) {
172
+ revealForWindow(val);
173
+ }
174
+ }, [decryptField, decrypting, revealForWindow, revealed, revealedValue]);
175
+
176
+ const handleHide = useCallback(() => {
177
+ setRevealed(false);
178
+ setRevealedValue(null);
179
+ if (revealTimerRef.current) clearTimeout(revealTimerRef.current);
180
+ }, []);
181
+
182
+ const handleLargeType = useCallback(async () => {
183
+ setMenuOpen(false);
184
+ const val = await decryptFieldSilent();
185
+ if (val != null) {
186
+ onShowLargeType(val);
187
+ }
188
+ }, [decryptFieldSilent, onShowLargeType]);
189
+
190
+ const handleCopyNonSensitive = useCallback(() => {
191
+ const valueToCopy = copyValue ?? value;
192
+ if (valueToCopy) {
193
+ navigator.clipboard.writeText(valueToCopy);
194
+ markCopied();
195
+ }
196
+ }, [copyValue, value, markCopied]);
197
+
198
+ const handleLargeTypeNonSensitive = useCallback(() => {
199
+ if (!value) return;
200
+ setMenuOpen(false);
201
+ onShowLargeType(value);
202
+ }, [onShowLargeType, value]);
203
+
204
+ const handleClickCopySensitive = useCallback(async () => {
205
+ if (!isHoverCopyMode || decrypting) return;
206
+ let val = revealedValue;
207
+ if (val == null) {
208
+ val = await decryptField();
209
+ if (val != null) setRevealedValue(val);
210
+ }
211
+ if (val != null) {
212
+ if (isRowHoverCopyMode) revealForWindow(val);
213
+ navigator.clipboard.writeText(val);
214
+ markCopied();
215
+ }
216
+ }, [decryptField, decrypting, isHoverCopyMode, isRowHoverCopyMode, markCopied, revealForWindow, revealedValue]);
217
+
218
+ const handleSensitiveContentClick = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
219
+ if (isRowHoverCopyMode || isSensitiveClickCopyMode) return;
220
+ event.stopPropagation();
221
+ if (isHoverCopyMode) {
222
+ void handleClickCopySensitive();
223
+ return;
224
+ }
225
+ void handleReveal();
226
+ }, [
227
+ handleClickCopySensitive,
228
+ handleReveal,
229
+ isHoverCopyMode,
230
+ isRowHoverCopyMode,
231
+ isSensitiveClickCopyMode,
232
+ ]);
233
+
234
+ if (!isSensitive) {
235
+ const actions = (
236
+ <div className="shrink-0 relative flex items-center gap-1">
237
+ <button
238
+ type="button"
239
+ onClick={(e) => {
240
+ e.stopPropagation();
241
+ handleCopyNonSensitive();
242
+ }}
243
+ className="inline-flex w-[56px] justify-start items-center gap-1 px-2 py-1 opacity-0 group-hover:opacity-100 transition-opacity font-mono text-[8px] uppercase tracking-widest text-[var(--color-text-faint,#9ca3af)] hover:text-[var(--color-text,#0a0a0a)]"
244
+ >
245
+ <Copy size={10} />
246
+ {copied ? 'Copied' : 'Copy'}
247
+ </button>
248
+ <button
249
+ ref={menuAnchorRef}
250
+ onClick={(e) => {
251
+ e.stopPropagation();
252
+ setMenuOpen(!menuOpen);
253
+ }}
254
+ className="p-1 opacity-0 group-hover:opacity-100 transition-opacity text-[var(--color-text-faint,#9ca3af)] hover:text-[var(--color-text,#0a0a0a)]"
255
+ aria-label={`Field actions for ${label}`}
256
+ >
257
+ <ChevronDown size={12} />
258
+ </button>
259
+ <Popover
260
+ isOpen={menuOpen}
261
+ onClose={() => setMenuOpen(false)}
262
+ anchorEl={menuAnchorRef.current}
263
+ anchor="right"
264
+ >
265
+ <div className="flex flex-col gap-1 min-w-[160px]">
266
+ <button
267
+ onClick={handleCopyNonSensitive}
268
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors"
269
+ >
270
+ <Copy size={10} />
271
+ Copy
272
+ </button>
273
+ <button
274
+ onClick={handleLargeTypeNonSensitive}
275
+ disabled={!value || disableLargeType}
276
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors disabled:opacity-40"
277
+ >
278
+ <Maximize2 size={10} />
279
+ Show in large type
280
+ </button>
281
+ </div>
282
+ </Popover>
283
+ </div>
284
+ );
285
+
286
+ if (renderMarkdown && value) {
287
+ return (
288
+ <div
289
+ className="flex flex-wrap lg:flex-nowrap gap-x-3 gap-y-1 py-2 px-2 cursor-pointer hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors group items-start"
290
+ onClick={handleCopyNonSensitive}
291
+ >
292
+ {actionsPosition === 'left' && actions}
293
+ <div className="w-24 shrink-0">
294
+ <span className="font-mono text-[9px] uppercase tracking-widest text-[var(--color-text-muted,#6b7280)]">
295
+ {copied ? 'Copied' : label}
296
+ </span>
297
+ </div>
298
+ <div className="basis-full lg:basis-auto lg:flex-1 min-w-0 prose-mono max-w-none [&>*:first-child]:mt-0 [&>*:last-child]:mb-0" dangerouslySetInnerHTML={{ __html: plainMarkdownHtml }} />
299
+ {actionsPosition === 'right' && actions}
300
+ </div>
301
+ );
302
+ }
303
+
304
+ return (
305
+ <div
306
+ className="flex flex-wrap lg:flex-nowrap items-center gap-x-3 gap-y-1 py-2 px-2 cursor-pointer hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors group"
307
+ onClick={handleCopyNonSensitive}
308
+ >
309
+ {actionsPosition === 'left' && actions}
310
+ <div className="w-24 shrink-0">
311
+ <span className="font-mono text-[9px] uppercase tracking-widest text-[var(--color-text-muted,#6b7280)]">
312
+ {label}
313
+ </span>
314
+ </div>
315
+ <div className="basis-full lg:basis-auto lg:flex-1 min-w-0 min-h-[24px] flex items-center">
316
+ <span className="block min-w-0 font-mono text-[11px] text-[var(--color-text,#0a0a0a)] truncate whitespace-nowrap">
317
+ {copied ? 'Copied' : (value || '--')}
318
+ </span>
319
+ {trailingValue}
320
+ </div>
321
+ {actionsPosition === 'right' && actions}
322
+ </div>
323
+ );
324
+ }
325
+
326
+ const sensitiveActions = canShowSensitiveActions ? (
327
+ <div className="shrink-0 relative flex items-center gap-1">
328
+ <button
329
+ type="button"
330
+ onClick={(e) => {
331
+ e.stopPropagation();
332
+ void handleCopySensitive();
333
+ }}
334
+ className="inline-flex w-[56px] justify-start items-center gap-1 px-2 py-1 opacity-0 group-hover:opacity-100 transition-opacity font-mono text-[8px] uppercase tracking-widest text-[var(--color-text-faint,#9ca3af)] hover:text-[var(--color-text,#0a0a0a)]"
335
+ >
336
+ <Copy size={10} />
337
+ {copied ? 'Copied' : 'Copy'}
338
+ </button>
339
+ <button
340
+ ref={menuAnchorRef}
341
+ onClick={(e) => {
342
+ e.stopPropagation();
343
+ setMenuOpen(!menuOpen);
344
+ }}
345
+ className="p-1 opacity-0 group-hover:opacity-100 transition-opacity text-[var(--color-text-faint,#9ca3af)] hover:text-[var(--color-text,#0a0a0a)]"
346
+ aria-label={`Field actions for ${label}`}
347
+ >
348
+ <ChevronDown size={12} />
349
+ </button>
350
+ <Popover
351
+ isOpen={menuOpen}
352
+ onClose={() => setMenuOpen(false)}
353
+ anchorEl={menuAnchorRef.current}
354
+ anchor="right"
355
+ >
356
+ <div className="flex flex-col gap-1 min-w-[170px]">
357
+ <button
358
+ onClick={handleCopySensitive}
359
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors"
360
+ >
361
+ <Copy size={10} />
362
+ Copy
363
+ </button>
364
+ {!isHoverCopyMode && !revealed && (
365
+ <button
366
+ onClick={handleReveal}
367
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors"
368
+ >
369
+ <Eye size={10} />
370
+ Reveal
371
+ </button>
372
+ )}
373
+ {!isHoverCopyMode && revealed && (
374
+ <button
375
+ onClick={handleHide}
376
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors"
377
+ >
378
+ <EyeOff size={10} />
379
+ Hide
380
+ </button>
381
+ )}
382
+ {!disableLargeType && (
383
+ <button
384
+ onClick={handleLargeType}
385
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors"
386
+ >
387
+ <Maximize2 size={10} />
388
+ Show in large type
389
+ </button>
390
+ )}
391
+ </div>
392
+ </Popover>
393
+ </div>
394
+ ) : null;
395
+
396
+ if (isMarkdownMode) {
397
+ const handleMarkdownClick = () => {
398
+ if (revealed && revealedValue != null) {
399
+ void handleCopySensitive();
400
+ } else {
401
+ void handleReveal();
402
+ }
403
+ };
404
+
405
+ const markdownActions = (
406
+ <div className="shrink-0 relative flex items-center gap-1">
407
+ <button
408
+ type="button"
409
+ onClick={(e) => {
410
+ e.stopPropagation();
411
+ void handleCopySensitive();
412
+ }}
413
+ className="inline-flex w-[56px] justify-start items-center gap-1 px-2 py-1 opacity-0 group-hover:opacity-100 transition-opacity font-mono text-[8px] uppercase tracking-widest text-[var(--color-text-faint,#9ca3af)] hover:text-[var(--color-text,#0a0a0a)]"
414
+ >
415
+ <Copy size={10} />
416
+ {copied ? 'Copied' : 'Copy'}
417
+ </button>
418
+ <button
419
+ ref={menuAnchorRef}
420
+ onClick={(e) => {
421
+ e.stopPropagation();
422
+ setMenuOpen(!menuOpen);
423
+ }}
424
+ className="p-1 opacity-0 group-hover:opacity-100 transition-opacity text-[var(--color-text-faint,#9ca3af)] hover:text-[var(--color-text,#0a0a0a)]"
425
+ aria-label={`Field actions for ${label}`}
426
+ >
427
+ <ChevronDown size={12} />
428
+ </button>
429
+ <Popover
430
+ isOpen={menuOpen}
431
+ onClose={() => setMenuOpen(false)}
432
+ anchorEl={menuAnchorRef.current}
433
+ anchor="right"
434
+ >
435
+ <div className="flex flex-col gap-1 min-w-[170px]">
436
+ <button
437
+ onClick={handleCopySensitive}
438
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors"
439
+ >
440
+ <Copy size={10} />
441
+ Copy
442
+ </button>
443
+ {!revealed ? (
444
+ <button
445
+ onClick={handleReveal}
446
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors"
447
+ >
448
+ <Eye size={10} />
449
+ Reveal
450
+ </button>
451
+ ) : (
452
+ <button
453
+ onClick={handleHide}
454
+ className="flex items-center gap-2 px-2 py-1.5 text-left font-mono text-[9px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] hover:bg-[var(--color-background-alt,#f4f4f5)] transition-colors"
455
+ >
456
+ <EyeOff size={10} />
457
+ Hide
458
+ </button>
459
+ )}
460
+ </div>
461
+ </Popover>
462
+ </div>
463
+ );
464
+
465
+ return (
466
+ <div
467
+ data-testid={`credential-field-row-${fieldKey}`}
468
+ className="flex flex-wrap lg:flex-nowrap gap-x-3 gap-y-1 py-2 px-2 group transition-colors cursor-pointer hover:bg-[var(--color-background-alt,#f4f4f5)] items-start"
469
+ onClick={handleMarkdownClick}
470
+ >
471
+ {actionsPosition === 'left' && markdownActions}
472
+ <div className="w-24 shrink-0">
473
+ <span className="font-mono text-[9px] uppercase tracking-widest text-[var(--color-text-muted,#6b7280)]">
474
+ {copied ? 'Copied' : label}
475
+ </span>
476
+ </div>
477
+ <div
478
+ data-testid={`credential-field-value-${fieldKey}`}
479
+ className="basis-full lg:basis-auto lg:flex-1 min-w-0"
480
+ >
481
+ {error ? (
482
+ <span className="font-mono text-[10px] text-[var(--color-danger,#ef4444)]">{error}</span>
483
+ ) : decrypting ? (
484
+ <Loader2 size={12} className="animate-spin text-[var(--color-text-muted,#6b7280)]" />
485
+ ) : revealed && revealedValue != null ? (
486
+ <div className="prose-mono max-w-none [&>*:first-child]:mt-0 [&>*:last-child]:mb-0" dangerouslySetInnerHTML={{ __html: markdownHtml }} />
487
+ ) : (
488
+ <span className="font-mono text-[11px] text-[var(--color-text-muted,#6b7280)] tracking-wider">
489
+ {'\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022'}
490
+ </span>
491
+ )}
492
+ </div>
493
+ {actionsPosition === 'right' && markdownActions}
494
+ </div>
495
+ );
496
+ }
497
+
498
+ return (
499
+ <div
500
+ data-testid={`credential-field-row-${fieldKey}`}
501
+ className={`flex flex-wrap lg:flex-nowrap gap-x-3 gap-y-1 py-2 px-2 group transition-colors rounded-sm items-center ${(isRowHoverCopyMode || isSensitiveClickCopyMode) ? 'cursor-pointer hover:bg-[var(--color-background-alt,#f4f4f5)]' : ''}`}
502
+ onClick={() => {
503
+ if (isRowHoverCopyMode) {
504
+ void handleClickCopySensitive();
505
+ return;
506
+ }
507
+ if (isSensitiveClickCopyMode) void handleCopySensitive();
508
+ }}
509
+ >
510
+ {actionsPosition === 'left' && sensitiveActions}
511
+ <div className="w-24 shrink-0">
512
+ <span className="font-mono text-[9px] uppercase tracking-widest text-[var(--color-text-muted,#6b7280)]">
513
+ {label}
514
+ </span>
515
+ </div>
516
+ <div
517
+ data-testid={`credential-field-value-${fieldKey}`}
518
+ className={`basis-full lg:basis-auto lg:flex-1 min-w-0 transition-colors rounded-sm ${isHoverCopyMode ? '' : 'cursor-pointer'} ${`${(isRowHoverCopyMode || isSensitiveClickCopyMode) ? 'min-h-[24px] flex items-center' : 'hover:bg-[var(--color-background-alt,#f4f4f5)] min-h-[24px] flex items-center'} px-1 py-0.5`}`}
519
+ onClick={handleSensitiveContentClick}
520
+ >
521
+ {error ? (
522
+ <span className="font-mono text-[10px] text-[var(--color-danger,#ef4444)]">{error}</span>
523
+ ) : decrypting ? (
524
+ <Loader2 size={12} className="animate-spin text-[var(--color-text-muted,#6b7280)]" />
525
+ ) : isRowHoverCopyMode ? (
526
+ revealed && revealedValue != null ? (
527
+ <div className="flex items-center gap-2 min-w-0">
528
+ <span className="block min-w-0 font-mono text-[11px] text-[var(--color-text,#0a0a0a)] truncate whitespace-nowrap">{revealedValue}</span>
529
+ <button
530
+ onClick={(e) => {
531
+ e.stopPropagation();
532
+ handleHide();
533
+ }}
534
+ className="shrink-0 text-[var(--color-text-faint,#9ca3af)] hover:text-[var(--color-text,#0a0a0a)] transition-colors"
535
+ >
536
+ <EyeOff size={12} />
537
+ </button>
538
+ </div>
539
+ ) : (
540
+ <div className="w-full">
541
+ <div
542
+ data-testid={`credential-field-barcode-${fieldKey}`}
543
+ className="h-4 w-full bg-[repeating-linear-gradient(90deg,var(--color-text,#000),var(--color-text,#000)_1px,transparent_1px,transparent_3px)] opacity-30"
544
+ />
545
+ <div
546
+ className="mt-1 h-1.5 w-full"
547
+ style={{
548
+ backgroundImage: 'repeating-linear-gradient(45deg, var(--color-text, #000), var(--color-text, #000) 5px, transparent 5px, transparent 10px)',
549
+ opacity: 0.1,
550
+ }}
551
+ />
552
+ <div className="mt-1 font-mono text-[9px] text-[var(--color-text-muted,#6b7280)] tracking-wider uppercase">
553
+ {copied ? 'Copied' : 'Click to reveal and copy'}
554
+ </div>
555
+ </div>
556
+ )
557
+ ) : isHoverCopyMode ? (
558
+ <span className="font-mono text-[10px] text-[var(--color-text-muted,#6b7280)] tracking-wider uppercase">
559
+ {copied ? 'Copied' : revealedValue != null ? 'Hover then click to copy' : 'Decrypting'}
560
+ </span>
561
+ ) : revealed && revealedValue != null ? (
562
+ <div className="flex items-center gap-2 min-w-0">
563
+ <span className="block min-w-0 font-mono text-[11px] text-[var(--color-text,#0a0a0a)] truncate whitespace-nowrap">{revealedValue}</span>
564
+ <button
565
+ onClick={(e) => {
566
+ e.stopPropagation();
567
+ handleHide();
568
+ }}
569
+ className="shrink-0 text-[var(--color-text-faint,#9ca3af)] hover:text-[var(--color-text,#0a0a0a)] transition-colors"
570
+ >
571
+ <EyeOff size={12} />
572
+ </button>
573
+ </div>
574
+ ) : (
575
+ <span className="font-mono text-[11px] text-[var(--color-text-muted,#6b7280)] tracking-wider">
576
+ {copied ? 'Copied' : '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022'}
577
+ </span>
578
+ )}
579
+ </div>
580
+ {actionsPosition === 'right' && sensitiveActions}
581
+ </div>
582
+ );
583
+ };