ethagent 1.1.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +126 -30
  3. package/package.json +7 -2
  4. package/src/app/FirstRun.tsx +190 -146
  5. package/src/app/FirstRunTimeline.tsx +47 -0
  6. package/src/app/input/AppInputProvider.tsx +1 -1
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -1
  8. package/src/chat/ChatBottomPane.tsx +0 -1
  9. package/src/chat/ChatInput.tsx +6 -6
  10. package/src/chat/ChatScreen.tsx +35 -15
  11. package/src/chat/ContextLimitView.tsx +4 -4
  12. package/src/chat/ContinuityEditReviewView.tsx +10 -22
  13. package/src/chat/CopyPicker.tsx +0 -1
  14. package/src/chat/MessageList.tsx +62 -45
  15. package/src/chat/PermissionPrompt.tsx +13 -9
  16. package/src/chat/PlanApprovalView.tsx +3 -3
  17. package/src/chat/ResumeView.tsx +1 -4
  18. package/src/chat/RewindView.tsx +2 -2
  19. package/src/chat/chatInputState.ts +1 -1
  20. package/src/chat/chatScreenUtils.ts +22 -11
  21. package/src/chat/chatSessionState.ts +2 -2
  22. package/src/chat/chatTurnOrchestrator.ts +16 -81
  23. package/src/chat/commands.ts +1 -1
  24. package/src/chat/textCursor.ts +1 -1
  25. package/src/chat/transcriptViewport.ts +2 -7
  26. package/src/cli/ResetConfirmView.tsx +1 -1
  27. package/src/cli/main.tsx +9 -3
  28. package/src/cli/preview.tsx +0 -5
  29. package/src/cli/updateNotice.ts +4 -2
  30. package/src/identity/continuity/editor.ts +7 -107
  31. package/src/identity/continuity/envelope.ts +1048 -40
  32. package/src/identity/continuity/history.ts +4 -4
  33. package/src/identity/continuity/localBackup.ts +249 -0
  34. package/src/identity/continuity/privateEdit/apply.ts +170 -0
  35. package/src/identity/continuity/privateEdit/diff.ts +82 -0
  36. package/src/identity/continuity/privateEdit/files.ts +23 -0
  37. package/src/identity/continuity/privateEdit/types.ts +28 -0
  38. package/src/identity/continuity/privateEdit.ts +10 -298
  39. package/src/identity/continuity/publicSkills.ts +8 -9
  40. package/src/identity/continuity/snapshots.ts +17 -6
  41. package/src/identity/continuity/storage/defaults.ts +111 -0
  42. package/src/identity/continuity/storage/files.ts +72 -0
  43. package/src/identity/continuity/storage/markdown.ts +81 -0
  44. package/src/identity/continuity/storage/paths.ts +24 -0
  45. package/src/identity/continuity/storage/scaffold.ts +124 -0
  46. package/src/identity/continuity/storage/status.ts +86 -0
  47. package/src/identity/continuity/storage/types.ts +27 -0
  48. package/src/identity/continuity/storage.ts +32 -507
  49. package/src/identity/continuity/zipWriter.ts +95 -0
  50. package/src/identity/crypto/backupEnvelope.ts +14 -247
  51. package/src/identity/crypto/eth.ts +7 -7
  52. package/src/identity/ens/agentRecords.ts +96 -0
  53. package/src/identity/ens/ensAutomation/contracts.ts +38 -0
  54. package/src/identity/ens/ensAutomation/delete.ts +80 -0
  55. package/src/identity/ens/ensAutomation/names.ts +14 -0
  56. package/src/identity/ens/ensAutomation/operators.ts +29 -0
  57. package/src/identity/ens/ensAutomation/read.ts +114 -0
  58. package/src/identity/ens/ensAutomation/root.ts +63 -0
  59. package/src/identity/ens/ensAutomation/setup.ts +284 -0
  60. package/src/identity/ens/ensAutomation/transactions.ts +107 -0
  61. package/src/identity/ens/ensAutomation/types.ts +126 -0
  62. package/src/identity/ens/ensAutomation.ts +29 -0
  63. package/src/identity/ens/ensLookup/client.ts +43 -0
  64. package/src/identity/ens/ensLookup/constants.ts +26 -0
  65. package/src/identity/ens/ensLookup/discovery.ts +70 -0
  66. package/src/identity/ens/ensLookup/names.ts +34 -0
  67. package/src/identity/ens/ensLookup/records.ts +45 -0
  68. package/src/identity/ens/ensLookup/resolve.ts +75 -0
  69. package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
  70. package/src/identity/ens/ensLookup/types.ts +38 -0
  71. package/src/identity/ens/ensLookup/validation.ts +72 -0
  72. package/src/identity/ens/ensLookup.ts +19 -0
  73. package/src/identity/ens/ensRegistration.ts +199 -0
  74. package/src/identity/ens/resolverDelegation.ts +48 -0
  75. package/src/identity/hub/IdentityHub.tsx +13 -817
  76. package/src/identity/hub/OperationalRoutes.tsx +370 -0
  77. package/src/identity/hub/Routes.tsx +361 -0
  78. package/src/identity/hub/advancedEnsValidation.ts +45 -0
  79. package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
  80. package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
  81. package/src/identity/hub/components/FlowTimeline.tsx +27 -0
  82. package/src/identity/hub/components/IdentitySummary.tsx +190 -0
  83. package/src/identity/hub/components/MenuScreen.tsx +237 -0
  84. package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
  85. package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
  86. package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
  87. package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
  88. package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
  89. package/src/identity/hub/effects/create.ts +310 -0
  90. package/src/identity/hub/effects/ens/flows.ts +218 -0
  91. package/src/identity/hub/effects/ens/index.ts +11 -0
  92. package/src/identity/hub/effects/ens/transactions.ts +239 -0
  93. package/src/identity/hub/effects/index.ts +74 -0
  94. package/src/identity/hub/effects/profile/profileState.ts +173 -0
  95. package/src/identity/hub/effects/publicProfile/index.ts +5 -0
  96. package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
  97. package/src/identity/hub/effects/rebackup/index.ts +7 -0
  98. package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
  99. package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
  100. package/src/identity/hub/effects/receipts.ts +46 -0
  101. package/src/identity/hub/effects/restore/apply.ts +112 -0
  102. package/src/identity/hub/effects/restore/auth.ts +159 -0
  103. package/src/identity/hub/effects/restore/discover.ts +86 -0
  104. package/src/identity/hub/effects/restore/envelopes.ts +21 -0
  105. package/src/identity/hub/effects/restore/fetch.ts +25 -0
  106. package/src/identity/hub/effects/restore/index.ts +22 -0
  107. package/src/identity/hub/effects/restore/recovery.ts +135 -0
  108. package/src/identity/hub/effects/restore/resolve.ts +102 -0
  109. package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
  110. package/src/identity/hub/effects/restore/shared.ts +91 -0
  111. package/src/identity/hub/effects/restoreAdmin.ts +93 -0
  112. package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
  113. package/src/identity/hub/effects/shared/snapshot.ts +336 -0
  114. package/src/identity/hub/effects/shared/sync.ts +190 -0
  115. package/src/identity/hub/effects/token-transfer/index.ts +6 -0
  116. package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
  117. package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
  118. package/src/identity/hub/effects/types.ts +53 -0
  119. package/src/identity/hub/effects/vault/preflight.ts +50 -0
  120. package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
  121. package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
  122. package/src/identity/hub/{screens → flows/continuity}/RecoveryConfirmScreen.tsx +28 -19
  123. package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
  124. package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
  125. package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
  126. package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
  127. package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
  128. package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
  129. package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
  130. package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
  131. package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
  132. package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
  133. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
  134. package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
  135. package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
  136. package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
  137. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
  138. package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
  139. package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
  140. package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
  141. package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
  142. package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
  143. package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
  144. package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
  145. package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
  146. package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
  147. package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +23 -44
  148. package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
  149. package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
  150. package/src/identity/hub/identityHubReducer.ts +164 -99
  151. package/src/identity/hub/model/continuity.ts +94 -0
  152. package/src/identity/hub/model/copy.ts +35 -0
  153. package/src/identity/hub/model/custody.ts +54 -0
  154. package/src/identity/hub/model/ens.ts +49 -0
  155. package/src/identity/hub/model/errors.ts +140 -0
  156. package/src/identity/hub/model/format.ts +15 -0
  157. package/src/identity/hub/model/identity.ts +94 -0
  158. package/src/identity/hub/model/network.ts +32 -0
  159. package/src/identity/hub/model/transfer.ts +57 -0
  160. package/src/identity/hub/operatorWallets.ts +131 -0
  161. package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
  162. package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
  163. package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
  164. package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
  165. package/src/identity/hub/reconciliation/index.ts +21 -0
  166. package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
  167. package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
  168. package/src/identity/hub/txGuard.ts +51 -0
  169. package/src/identity/hub/types.ts +17 -0
  170. package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
  171. package/src/identity/hub/useIdentityHubController.ts +396 -0
  172. package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
  173. package/src/identity/hub/utils.ts +79 -0
  174. package/src/identity/identityCompat.ts +34 -0
  175. package/src/identity/profile/agentIcon.ts +61 -0
  176. package/src/identity/profile/imagePicker.ts +12 -12
  177. package/src/identity/registry/erc8004/abi.ts +14 -0
  178. package/src/identity/registry/erc8004/chains.ts +150 -0
  179. package/src/identity/registry/erc8004/client.ts +11 -0
  180. package/src/identity/registry/erc8004/discovery.ts +511 -0
  181. package/src/identity/registry/erc8004/metadata.ts +335 -0
  182. package/src/identity/registry/erc8004/ownership.ts +121 -0
  183. package/src/identity/registry/erc8004/preflight.ts +123 -0
  184. package/src/identity/registry/erc8004/transactions.ts +77 -0
  185. package/src/identity/registry/erc8004/types.ts +88 -0
  186. package/src/identity/registry/erc8004/uri.ts +59 -0
  187. package/src/identity/registry/erc8004/utils.ts +58 -0
  188. package/src/identity/registry/erc8004.ts +53 -1106
  189. package/src/identity/registry/fieldParsers.ts +28 -0
  190. package/src/identity/registry/operatorVault/bytecode.ts +98 -0
  191. package/src/identity/registry/operatorVault/constants.ts +38 -0
  192. package/src/identity/registry/operatorVault/read.ts +246 -0
  193. package/src/identity/registry/operatorVault/transactions.ts +81 -0
  194. package/src/identity/registry/operatorVault.ts +44 -0
  195. package/src/identity/storage/ipfs.ts +26 -24
  196. package/src/identity/wallet/browserWallet/gas.ts +41 -0
  197. package/src/identity/wallet/browserWallet/html.ts +106 -0
  198. package/src/identity/wallet/browserWallet/http.ts +28 -0
  199. package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
  200. package/src/identity/wallet/browserWallet/requests.ts +191 -0
  201. package/src/identity/wallet/browserWallet/session.ts +325 -0
  202. package/src/identity/wallet/browserWallet/types.ts +192 -0
  203. package/src/identity/wallet/browserWallet/validation.ts +74 -0
  204. package/src/identity/wallet/browserWallet.ts +30 -393
  205. package/src/identity/wallet/page/constants.ts +5 -0
  206. package/src/identity/wallet/page/controller.ts +251 -0
  207. package/src/identity/wallet/page/copy.ts +340 -0
  208. package/src/identity/wallet/page/grainient.ts +278 -0
  209. package/src/identity/wallet/page/html.ts +28 -0
  210. package/src/identity/wallet/page/markup.ts +50 -0
  211. package/src/identity/wallet/page/state.ts +9 -0
  212. package/src/identity/wallet/page/styles/base.ts +259 -0
  213. package/src/identity/wallet/page/styles/components.ts +262 -0
  214. package/src/identity/wallet/page/styles/index.ts +5 -0
  215. package/src/identity/wallet/page/styles/responsive.ts +247 -0
  216. package/src/identity/wallet/page/types.ts +47 -0
  217. package/src/identity/wallet/page/view.ts +535 -0
  218. package/src/identity/wallet/page/walletProvider.ts +70 -0
  219. package/src/identity/wallet/page.tsx +38 -0
  220. package/src/identity/wallet/walletPurposeCompat.ts +27 -0
  221. package/src/mcp/manager.ts +0 -1
  222. package/src/models/ModelPicker.tsx +36 -30
  223. package/src/models/catalog.ts +5 -2
  224. package/src/models/huggingface.ts +9 -9
  225. package/src/models/llamacpp.ts +13 -13
  226. package/src/models/modelDisplay.ts +75 -0
  227. package/src/models/modelPickerOptions.ts +16 -3
  228. package/src/models/modelRecommendation.ts +0 -1
  229. package/src/providers/errors.ts +16 -0
  230. package/src/providers/gemini.ts +252 -39
  231. package/src/providers/registry.ts +2 -2
  232. package/src/providers/retry.ts +1 -1
  233. package/src/runtime/sessionMode.ts +1 -1
  234. package/src/runtime/systemPrompt.ts +2 -0
  235. package/src/runtime/toolExecution.ts +18 -22
  236. package/src/runtime/toolIntent.ts +0 -20
  237. package/src/runtime/turn.ts +0 -92
  238. package/src/storage/atomicWrite.ts +4 -1
  239. package/src/storage/config.ts +181 -5
  240. package/src/storage/identity.ts +9 -3
  241. package/src/storage/secrets.ts +2 -2
  242. package/src/tools/bashSafety.ts +8 -0
  243. package/src/tools/changeDirectoryTool.ts +1 -1
  244. package/src/tools/deleteFileTool.ts +4 -4
  245. package/src/tools/editTool.ts +4 -4
  246. package/src/tools/editUtils.ts +5 -5
  247. package/src/tools/privateContinuityEditTool.ts +4 -5
  248. package/src/tools/privateContinuityReadTool.ts +1 -2
  249. package/src/tools/registry.ts +30 -0
  250. package/src/tools/writeFileTool.ts +5 -5
  251. package/src/ui/BrandSplash.tsx +20 -85
  252. package/src/ui/ProgressBar.tsx +3 -5
  253. package/src/ui/Select.tsx +20 -8
  254. package/src/ui/Spinner.tsx +38 -3
  255. package/src/ui/Surface.tsx +2 -2
  256. package/src/ui/TextInput.tsx +63 -20
  257. package/src/ui/theme.ts +7 -34
  258. package/src/utils/openExternal.ts +21 -0
  259. package/src/utils/withRetry.ts +47 -3
  260. package/src/identity/hub/identityHubEffects.ts +0 -937
  261. package/src/identity/hub/identityHubModel.ts +0 -371
  262. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -156
  263. package/src/identity/hub/screens/EditProfileFlow.tsx +0 -146
  264. package/src/identity/hub/screens/IdentitySummary.tsx +0 -106
  265. package/src/identity/hub/screens/MenuScreen.tsx +0 -117
  266. package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
  267. package/src/identity/wallet/wallet-page/wallet.html +0 -1202
  268. /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
@@ -0,0 +1,340 @@
1
+ import { config } from './state.js'
2
+ import { glyphs } from './html.js'
3
+
4
+ export const CHAINS: Record<string, { name: string }> = {
5
+ "0x1": { name: "Ethereum Mainnet" },
6
+ "0xaa36a7": { name: "Sepolia" },
7
+ "0x2105": { name: "Base" },
8
+ "0x14a34": { name: "Base Sepolia" },
9
+ };
10
+
11
+ export interface FlowCopy {
12
+ accent: string;
13
+ tabTitle: string;
14
+ label: string;
15
+ title: string;
16
+ detail: string | null;
17
+ }
18
+
19
+ export const FLOW_COPY: Record<string, FlowCopy> = {
20
+ account: { accent: "sign", tabTitle: "Connect Wallet", label: "Wallet Request", title: "Connect Wallet", detail: null },
21
+ sign: { accent: "sign", tabTitle: "Sign Message", label: "Signature Request", title: "Sign Message", detail: "message" },
22
+ "sign-transaction": { accent: "transaction", tabTitle: "Sign And Save", label: "Identity Signature", title: "Sign And Save", detail: "message" },
23
+ transaction: { accent: "transaction", tabTitle: "Submit Transaction", label: "Transaction Request", title: "Submit Transaction", detail: "registry" },
24
+ };
25
+
26
+ export const TRANSACTION_TITLES: Record<string, string> = {
27
+ "register-agent": "Mint Agent Token",
28
+ "create-agent": "Create Agent",
29
+ "update-ens-records": "Use ENS Owner Wallet",
30
+ "clear-ens-records": "Use ENS Owner Wallet",
31
+ "create-simple-ens-subdomain": "Use Connected Wallet",
32
+ "set-simple-ens-records": "Use Connected Wallet",
33
+ "create-agent-ens-subdomain": "Owner Wallet Required",
34
+ "set-agent-ens-records": "Owner Wallet Required",
35
+ "publish-transfer-snapshot": "Switch Back to Sender Wallet",
36
+ };
37
+
38
+ export function transactionPurposeTitle(): string {
39
+ const key = config.purpose || "";
40
+ const fromPurpose = PURPOSE_COPY[key]?.flowTitle;
41
+ const explicit = TRANSACTION_TITLES[key];
42
+ return fromPurpose || explicit || FLOW_COPY.transaction!.title;
43
+ }
44
+
45
+ export const STATE_TITLES = {
46
+ connecting: "Connecting Wallet",
47
+ approveSign: "Sign Message",
48
+ preparingTransaction: "Preparing Transaction",
49
+ approveTransaction: "Review Transaction",
50
+ error: "Wallet Error",
51
+ default: "Wallet Request",
52
+ };
53
+
54
+ export interface SubCopy { text: string; hint: string; }
55
+ export interface PurposeCopyEntry {
56
+ flowTitle: string;
57
+ account?: SubCopy;
58
+ sign?: SubCopy;
59
+ prepare?: SubCopy;
60
+ transaction?: SubCopy;
61
+ errorContext?: string;
62
+ }
63
+
64
+ export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
65
+ "connect-operator-wallet": {
66
+ flowTitle: "Connect Wallet",
67
+ account: { text: "Connect Wallet", hint: "Reads your operator wallet address so the agent ENS subdomain can resolve to it. No signature or transaction." },
68
+ prepare: { text: "Reading Operator Wallet...", hint: "Return to the terminal." },
69
+ },
70
+ "create-agent": {
71
+ flowTitle: "Create Agent",
72
+ sign: { text: "Sign With Owner Wallet", hint: "Creates encrypted recovery access. No token approval." },
73
+ prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
74
+ transaction: { text: "Use Owner Wallet", hint: "Submits one onchain transaction that mints a new ERC-8004 agent token to this wallet." },
75
+ },
76
+ "restore-owner-wallet": {
77
+ flowTitle: "Owner Wallet Required",
78
+ sign: { text: "Sign With Owner Wallet", hint: "Decrypts this snapshot. No transaction." },
79
+ prepare: { text: "Verifying Owner Wallet...", hint: "Return to the terminal." },
80
+ },
81
+ "restore-operator-wallet": {
82
+ flowTitle: "Operator Wallet Required",
83
+ sign: { text: "Sign With Operator Wallet", hint: "Decrypts this snapshot. No transaction." },
84
+ prepare: { text: "Verifying Operator Wallet...", hint: "Return to the terminal." },
85
+ },
86
+ "update-snapshot-owner": {
87
+ flowTitle: "Owner Wallet Required",
88
+ sign: { text: "Sign With Owner Wallet", hint: "Encrypts local files before publishing. Owner wallet is required because this update changes ownership-protected fields." },
89
+ prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
90
+ transaction: { text: "Use Owner Wallet", hint: "Publishes the updated snapshot to the ERC-8004 token URI." },
91
+ },
92
+ "update-snapshot-operator": {
93
+ flowTitle: "Operator Wallet Save",
94
+ sign: { text: "Sign With Operator Wallet", hint: "Signs the encrypted snapshot for restore access." },
95
+ prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
96
+ transaction: { text: "Use Operator Wallet", hint: "Rotates the ERC-8004 token URI through the operator delegation vault." },
97
+ },
98
+ "update-snapshot-connected": {
99
+ flowTitle: "Save Snapshot",
100
+ sign: { text: "Sign With Connected Wallet", hint: "Encrypts local files before publishing. Single-wallet setup; no token approval." },
101
+ prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
102
+ transaction: { text: "Use Connected Wallet", hint: "Publishes the updated snapshot to the ERC-8004 token URI." },
103
+ },
104
+ "update-ens": {
105
+ flowTitle: "Update ENS in Agent Snapshot",
106
+ sign: { text: "Sign With Owner Wallet", hint: "Saves a new agent snapshot with this ENS name. No onchain ENS records change in this signature." },
107
+ prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
108
+ transaction: { text: "Use Owner Wallet", hint: "Publishes the updated ERC-8004 token URI pointing to the new snapshot." },
109
+ },
110
+ "clear-ens": {
111
+ flowTitle: "Clear ENS from Agent Snapshot",
112
+ sign: { text: "Sign With Owner Wallet", hint: "Saves a new agent snapshot with no ENS name. No onchain ENS records change in this signature." },
113
+ prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
114
+ transaction: { text: "Use Owner Wallet", hint: "Publishes the updated ERC-8004 token URI pointing to the new snapshot." },
115
+ },
116
+ "update-profile-owner": {
117
+ flowTitle: "Owner Wallet Required",
118
+ sign: { text: "Sign With Owner Wallet", hint: "Saves public profile changes. Owner wallet is required because this update changes ownership-protected fields." },
119
+ prepare: { text: "Preparing Profile Update...", hint: "Keep this page open." },
120
+ transaction: { text: "Use Owner Wallet", hint: "Publishes the updated public profile to the ERC-8004 token URI." },
121
+ },
122
+ "update-profile-operator": {
123
+ flowTitle: "Operator Wallet Profile Update",
124
+ sign: { text: "Sign With Operator Wallet", hint: "Signs the encrypted snapshot for restore access." },
125
+ prepare: { text: "Preparing Profile Update...", hint: "Keep this page open." },
126
+ transaction: { text: "Use Operator Wallet", hint: "Publishes the updated agent-card pointer to the ENS profile record on Ethereum Mainnet." },
127
+ },
128
+ "update-profile-connected": {
129
+ flowTitle: "Update Public Profile",
130
+ sign: { text: "Sign With Connected Wallet", hint: "Saves public profile changes. Single-wallet setup; no token approval." },
131
+ prepare: { text: "Preparing Profile Update...", hint: "Keep this page open." },
132
+ transaction: { text: "Use Connected Wallet", hint: "Publishes the updated public profile to the ERC-8004 token URI." },
133
+ },
134
+ "update-ens-records": {
135
+ flowTitle: "Update ENS Records",
136
+ sign: { text: "Sign With ENS Owner Wallet", hint: "Authorizes ENS record changes. No token approval." },
137
+ prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
138
+ transaction: { text: "Use ENS Owner Wallet", hint: "Submit one Ethereum Mainnet ENS record transaction." },
139
+ },
140
+ "clear-ens-records": {
141
+ flowTitle: "Unlink ENS",
142
+ sign: { text: "Sign With ENS Owner Wallet", hint: "Authorizes clearing ethagent ENS text records. No token approval." },
143
+ prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
144
+ transaction: { text: "Use ENS Owner Wallet", hint: "Submit one Ethereum Mainnet transaction to clear ethagent ENS text records." },
145
+ },
146
+ "create-simple-ens-subdomain": {
147
+ flowTitle: "Review ENS Subdomain Creation",
148
+ sign: { text: "Sign With Connected Wallet", hint: "Connected wallet owns the ENS root and is creating the agent subdomain." },
149
+ prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
150
+ transaction: { text: "Use Connected Wallet", hint: "Creates the agent ENS subdomain on Ethereum Mainnet. Must come from the parent name's owner." },
151
+ },
152
+ "set-simple-ens-records": {
153
+ flowTitle: "Apply ENS Records",
154
+ sign: { text: "Sign With Connected Wallet", hint: "Connected wallet writes the agent ENS records." },
155
+ prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
156
+ transaction: { text: "Use Connected Wallet", hint: "Writes the agent ENS text records onchain. Must come from the name's controller." },
157
+ },
158
+ "create-agent-ens-subdomain": {
159
+ flowTitle: "Review Agent ENS Subdomain",
160
+ sign: { text: "Sign With Owner Wallet", hint: "Owner wallet controls the ENS root that the agent subdomain hangs off." },
161
+ prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
162
+ transaction: { text: "Use Owner Wallet", hint: "Creates the agent ENS subdomain on Ethereum Mainnet. Must come from the parent name's owner wallet." },
163
+ },
164
+ "set-agent-ens-records": {
165
+ flowTitle: "Apply Agent ENS Records",
166
+ sign: { text: "Sign With Owner Wallet", hint: "Owner wallet writes the agent ENS records pointing at the operator wallet." },
167
+ prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
168
+ transaction: { text: "Use Owner Wallet", hint: "Writes the agent ENS text and address records onchain. Must come from the name's controller." },
169
+ },
170
+ "update-operators": {
171
+ flowTitle: "Owner Wallet Required",
172
+ sign: { text: "Sign With Owner Wallet", hint: "Authorizes the new operator wallet access list. No token approval." },
173
+ prepare: { text: "Preparing Operator Wallets...", hint: "Keep this page open." },
174
+ transaction: { text: "Use Owner Wallet", hint: "Publishes the updated operator wallet access list onchain." },
175
+ },
176
+ "operator-proof": {
177
+ flowTitle: "Operator Wallet Required",
178
+ sign: { text: "Sign With Operator Wallet", hint: "Creates restore access. No token approval." },
179
+ prepare: { text: "Verifying Operator Wallet...", hint: "Return to the terminal." },
180
+ },
181
+ "authorize-operator-wallet-resolver": {
182
+ flowTitle: "Owner Wallet Required",
183
+ prepare: { text: "Preparing Resolver Approval...", hint: "Keep this page open." },
184
+ transaction: { text: "Use Owner Wallet", hint: "Approves the operator wallet to write ENS records for this name. No token approval." },
185
+ },
186
+ "revoke-operator-wallet-resolver": {
187
+ flowTitle: "Owner Wallet Required",
188
+ prepare: { text: "Preparing Resolver Revocation...", hint: "Keep this page open." },
189
+ transaction: { text: "Use Owner Wallet", hint: "Revokes the operator wallet's ENS record write access for this name." },
190
+ },
191
+ "reconcile-resolver-approvals": {
192
+ flowTitle: "Owner Wallet Required",
193
+ prepare: { text: "Preparing Records Fix...", hint: "Keep this page open." },
194
+ transaction: { text: "Use Owner Wallet", hint: "Brings ENS resolver approvals in sync with the authorized operator wallet list." },
195
+ },
196
+ "sync-operator-vault": {
197
+ flowTitle: "Owner Wallet Required",
198
+ prepare: { text: "Preparing Vault Operator Update...", hint: "Keep this page open." },
199
+ transaction: { text: "Use Owner Wallet", hint: "Updates the operator delegation vault's metadata-operator list so authorized operator wallets can rotate the agent URI." },
200
+ },
201
+ "refetch-snapshot": {
202
+ flowTitle: "Refetch Latest Snapshot",
203
+ sign: { text: "Sign With Any Authorized Wallet", hint: "The owner wallet or any authorized operator wallet can decrypt this snapshot. No transaction." },
204
+ prepare: { text: "Verifying Wallet...", hint: "Return to the terminal." },
205
+ },
206
+ "prepare-transfer-sender": {
207
+ flowTitle: "Use Sender Wallet",
208
+ sign: { text: "Sign With Sender Wallet", hint: "Creates sender restore access. No token approval." },
209
+ prepare: { text: "Verifying Sender Wallet...", hint: "Return to the terminal." },
210
+ },
211
+ "prepare-transfer-target": {
212
+ flowTitle: "Switch to Receiver Wallet",
213
+ sign: { text: "Sign With Receiver Wallet", hint: "Creates receiver restore access. No token approval." },
214
+ prepare: { text: "Verifying Receiver Wallet...", hint: "Return to the terminal." },
215
+ },
216
+ "publish-transfer-snapshot": {
217
+ flowTitle: "Switch Back to Sender Wallet",
218
+ prepare: { text: "Preparing Token Update...", hint: "Keep this page open." },
219
+ transaction: { text: "Use Sender Wallet", hint: "Submits one transaction to publish the transfer snapshot to the ERC-8004 token URI." },
220
+ },
221
+ "deploy-agent-vault": {
222
+ flowTitle: "Deploy Operator Delegation Vault",
223
+ prepare: { text: "Preparing Vault Deploy...", hint: "Keep this page open." },
224
+ transaction: { text: "Use Owner Wallet", hint: "Deploys the operator delegation vault contract onchain. One-time setup per agent." },
225
+ errorContext: "While submitting the operator delegation vault deploy",
226
+ },
227
+ "deposit-agent-vault": {
228
+ flowTitle: "Deposit Token Into Vault",
229
+ prepare: { text: "Preparing Vault Deposit...", hint: "Keep this page open." },
230
+ transaction: { text: "Use Owner Wallet", hint: "Sends the agent token to the operator delegation vault so the vault can save updates on your behalf." },
231
+ errorContext: "While submitting the operator delegation vault deposit",
232
+ },
233
+ "unwrap-agent-vault": {
234
+ flowTitle: "Unwrap Token From Vault",
235
+ prepare: { text: "Preparing Vault Unwrap...", hint: "Keep this page open." },
236
+ transaction: { text: "Use Owner Wallet", hint: "Returns the agent token from the operator delegation vault to your owner wallet." },
237
+ },
238
+ "rotate-agent-uri-vault-owner": {
239
+ flowTitle: "Save Update Through Vault",
240
+ sign: { text: "Sign With Owner Wallet", hint: "Approves the new snapshot before saving onchain. No token approval." },
241
+ prepare: { text: "Preparing Update...", hint: "Keep this page open." },
242
+ transaction: { text: "Use Owner Wallet", hint: "Saves your update onchain through the operator delegation vault. The vault holds your token, so updates go through it." },
243
+ },
244
+ "rotate-agent-uri-vault-operator": {
245
+ flowTitle: "Save Update Through Vault",
246
+ sign: { text: "Sign With Operator Wallet", hint: "Approves the new snapshot before saving onchain. No token approval." },
247
+ prepare: { text: "Preparing Update...", hint: "Keep this page open." },
248
+ transaction: { text: "Use Operator Wallet", hint: "Rotates the ERC-8004 token URI through the operator delegation vault. The vault holds your token, so the operator wallet calls the vault to publish." },
249
+ },
250
+ "withdraw-vault": {
251
+ flowTitle: "Withdraw Token From Vault",
252
+ prepare: { text: "Preparing Token Withdrawal...", hint: "Keep this page open." },
253
+ transaction: { text: "Use Owner Wallet", hint: "Temporarily returns the agent token from the vault to your owner wallet. Vault stays configured so you can redeposit later." },
254
+ errorContext: "While submitting the operator delegation vault withdraw",
255
+ },
256
+ "register-root-commit": {
257
+ flowTitle: "Commit ENS Name",
258
+ prepare: { text: "Preparing ENS Commit...", hint: "Keep this page open." },
259
+ transaction: { text: "Use Connected Wallet", hint: "Submits an ENS commitment, the first of two transactions. The actual registration runs about 60 seconds later." },
260
+ errorContext: "While committing the ENS name registration",
261
+ },
262
+ "register-root-tx": {
263
+ flowTitle: "Register ENS Name",
264
+ prepare: { text: "Preparing ENS Registration...", hint: "Keep this page open." },
265
+ transaction: { text: "Use Connected Wallet", hint: "Pays the 1-year rent and registers the name. The connected wallet becomes the owner." },
266
+ errorContext: "While registering the ENS name",
267
+ },
268
+ "delete-ens-subdomain": {
269
+ flowTitle: "Delete ENS Subdomain",
270
+ prepare: { text: "Preparing Subdomain Deletion...", hint: "Keep this page open." },
271
+ transaction: { text: "Use Owner Wallet", hint: "Clears the subdomain entry in the parent ENS name. After this, the label is freed for reuse." },
272
+ errorContext: "While deleting the ENS subdomain",
273
+ },
274
+ };
275
+
276
+ export function purposeCopy(): PurposeCopyEntry {
277
+ const key = config.purpose;
278
+ const copy = PURPOSE_COPY[key || ""];
279
+ if (!copy) {
280
+ throw new Error(
281
+ "Wallet purpose '" + String(key) + "' has no copy entry; refusing to render generic wallet prompt."
282
+ );
283
+ }
284
+ return copy;
285
+ }
286
+ export function requirePurposeSubCopy(key: keyof PurposeCopyEntry): SubCopy {
287
+ const copy = purposeCopy();
288
+ const sub = copy[key] as SubCopy | undefined;
289
+ if (!sub || typeof sub.text !== "string" || typeof sub.hint !== "string") {
290
+ throw new Error(
291
+ "Wallet purpose '" + String(config.purpose) + "' missing '" + String(key) + "' copy; refusing to render generic wallet prompt."
292
+ );
293
+ }
294
+ return sub;
295
+ }
296
+ export function accountCopy(): SubCopy { return requirePurposeSubCopy("account"); }
297
+ export const ENS_PURPOSES: ReadonlySet<string> = new Set([
298
+ "create-simple-ens-subdomain",
299
+ "set-simple-ens-records",
300
+ "create-agent-ens-subdomain",
301
+ "set-agent-ens-records",
302
+ "update-ens-records",
303
+ "clear-ens-records",
304
+ "authorize-operator-wallet-resolver",
305
+ "revoke-operator-wallet-resolver",
306
+ "reconcile-resolver-approvals",
307
+ ]);
308
+ export function isEnsPurpose(purpose: string | undefined): boolean {
309
+ return !!purpose && ENS_PURPOSES.has(purpose);
310
+ }
311
+ export function ensTokenChainHint(): string {
312
+ const name = typeof config.tokenChainName === "string" ? config.tokenChainName.trim() : "";
313
+ if (!name || !isEnsPurpose(config.purpose)) return "";
314
+ return " Your agent token stays on " + name + "; ENS lives on Ethereum Mainnet.";
315
+ }
316
+ export function transactionCopy(): SubCopy {
317
+ const txCopy = requirePurposeSubCopy("transaction");
318
+ const expected = config.expectedAccount ? " Expected Wallet: " + shortAddr(config.expectedAccount) + "." : "";
319
+ const text = config.expectedAccount ? txCopy.text + " (" + shortAddr(config.expectedAccount) + ")" : txCopy.text;
320
+ return { text, hint: txCopy.hint + expected + ensTokenChainHint() };
321
+ }
322
+ export function signCopy(): SubCopy {
323
+ const sigCopy = requirePurposeSubCopy("sign");
324
+ const expected = config.expectedAccount ? " Expected Wallet: " + shortAddr(config.expectedAccount) + "." : "";
325
+ const text = config.expectedAccount ? sigCopy.text + " (" + shortAddr(config.expectedAccount) + ")" : sigCopy.text;
326
+ return { text, hint: sigCopy.hint + expected + ensTokenChainHint() };
327
+ }
328
+
329
+ export function chainLabel(hex?: string): string {
330
+ const k = String(hex || "").toLowerCase();
331
+ return (CHAINS[k] && CHAINS[k].name) || ("chain " + k);
332
+ }
333
+ export function shortAddr(addr?: string): string {
334
+ if (!addr || typeof addr !== "string") return "";
335
+ if (addr.length <= 14) return addr;
336
+ return addr.slice(0, 6) + glyphs.ellipsis + addr.slice(-4);
337
+ }
338
+ export function isTransactionFlow(): boolean {
339
+ return config.kind === "transaction" || config.kind === "sign-transaction";
340
+ }
@@ -0,0 +1,278 @@
1
+ interface GrainientOptions {
2
+ timeSpeed?: number;
3
+ colorBalance?: number;
4
+ warpStrength?: number;
5
+ warpFrequency?: number;
6
+ warpSpeed?: number;
7
+ warpAmplitude?: number;
8
+ blendAngle?: number;
9
+ blendSoftness?: number;
10
+ rotationAmount?: number;
11
+ noiseScale?: number;
12
+ grainAmount?: number;
13
+ grainScale?: number;
14
+ grainAnimated?: boolean;
15
+ contrast?: number;
16
+ gamma?: number;
17
+ saturation?: number;
18
+ centerX?: number;
19
+ centerY?: number;
20
+ zoom?: number;
21
+ color1?: string;
22
+ color2?: string;
23
+ color3?: string;
24
+ }
25
+
26
+ function hexToRgb(hex: string): [number, number, number] {
27
+ const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
28
+ if (!m) return [1, 1, 1];
29
+ return [
30
+ parseInt(m[1]!, 16) / 255,
31
+ parseInt(m[2]!, 16) / 255,
32
+ parseInt(m[3]!, 16) / 255,
33
+ ];
34
+ }
35
+
36
+ const GRAINIENT_VERTEX = `#version 300 es
37
+ in vec2 position;
38
+ void main() {
39
+ gl_Position = vec4(position, 0.0, 1.0);
40
+ }
41
+ `;
42
+
43
+ const GRAINIENT_FRAGMENT = `#version 300 es
44
+ precision highp float;
45
+ uniform vec2 iResolution;
46
+ uniform float iTime;
47
+ uniform float uTimeSpeed;
48
+ uniform float uColorBalance;
49
+ uniform float uWarpStrength;
50
+ uniform float uWarpFrequency;
51
+ uniform float uWarpSpeed;
52
+ uniform float uWarpAmplitude;
53
+ uniform float uBlendAngle;
54
+ uniform float uBlendSoftness;
55
+ uniform float uRotationAmount;
56
+ uniform float uNoiseScale;
57
+ uniform float uGrainAmount;
58
+ uniform float uGrainScale;
59
+ uniform float uGrainAnimated;
60
+ uniform float uContrast;
61
+ uniform float uGamma;
62
+ uniform float uSaturation;
63
+ uniform vec2 uCenterOffset;
64
+ uniform float uZoom;
65
+ uniform vec3 uColor1;
66
+ uniform vec3 uColor2;
67
+ uniform vec3 uColor3;
68
+ out vec4 fragColor;
69
+ #define S(a,b,t) smoothstep(a,b,t)
70
+ mat2 Rot(float a){float s=sin(a),c=cos(a);return mat2(c,-s,s,c);}
71
+ vec2 hash(vec2 p){p=vec2(dot(p,vec2(2127.1,81.17)),dot(p,vec2(1269.5,283.37)));return fract(sin(p)*43758.5453);}
72
+ float noise(vec2 p){vec2 i=floor(p),f=fract(p),u=f*f*(3.0-2.0*f);float n=mix(mix(dot(-1.0+2.0*hash(i+vec2(0.0,0.0)),f-vec2(0.0,0.0)),dot(-1.0+2.0*hash(i+vec2(1.0,0.0)),f-vec2(1.0,0.0)),u.x),mix(dot(-1.0+2.0*hash(i+vec2(0.0,1.0)),f-vec2(0.0,1.0)),dot(-1.0+2.0*hash(i+vec2(1.0,1.0)),f-vec2(1.0,1.0)),u.x),u.y);return 0.5+0.5*n;}
73
+ void mainImage(out vec4 o, vec2 C){
74
+ float t=iTime*uTimeSpeed;
75
+ vec2 uv=C/iResolution.xy;
76
+ float ratio=iResolution.x/iResolution.y;
77
+ vec2 tuv=uv-0.5+uCenterOffset;
78
+ tuv/=max(uZoom,0.001);
79
+
80
+ float degree=noise(vec2(t*0.1,tuv.x*tuv.y)*uNoiseScale);
81
+ tuv.y*=1.0/ratio;
82
+ tuv*=Rot(radians((degree-0.5)*uRotationAmount+180.0));
83
+ tuv.y*=ratio;
84
+
85
+ float frequency=uWarpFrequency;
86
+ float ws=max(uWarpStrength,0.001);
87
+ float amplitude=uWarpAmplitude/ws;
88
+ float warpTime=t*uWarpSpeed;
89
+ tuv.x+=sin(tuv.y*frequency+warpTime)/amplitude;
90
+ tuv.y+=sin(tuv.x*(frequency*1.5)+warpTime)/(amplitude*0.5);
91
+
92
+ vec3 colLav=uColor1;
93
+ vec3 colOrg=uColor2;
94
+ vec3 colDark=uColor3;
95
+ float b=uColorBalance;
96
+ float s=max(uBlendSoftness,0.0);
97
+ mat2 blendRot=Rot(radians(uBlendAngle));
98
+ float blendX=(tuv*blendRot).x;
99
+ float edge0=-0.3-b-s;
100
+ float edge1=0.2-b+s;
101
+ float v0=0.5-b+s;
102
+ float v1=-0.3-b-s;
103
+ vec3 layer1=mix(colDark,colOrg,S(edge0,edge1,blendX));
104
+ vec3 layer2=mix(colOrg,colLav,S(edge0,edge1,blendX));
105
+ vec3 col=mix(layer1,layer2,S(v0,v1,tuv.y));
106
+
107
+ vec2 grainUv=uv*max(uGrainScale,0.001);
108
+ if(uGrainAnimated>0.5){grainUv+=vec2(iTime*0.05);}
109
+ float grain=fract(sin(dot(grainUv,vec2(12.9898,78.233)))*43758.5453);
110
+ col+=(grain-0.5)*uGrainAmount;
111
+
112
+ col=(col-0.5)*uContrast+0.5;
113
+ float luma=dot(col,vec3(0.2126,0.7152,0.0722));
114
+ col=mix(vec3(luma),col,uSaturation);
115
+ col=pow(max(col,0.0),vec3(1.0/max(uGamma,0.001)));
116
+ col=clamp(col,0.0,1.0);
117
+
118
+ o=vec4(col,1.0);
119
+ }
120
+ void main(){
121
+ vec4 o=vec4(0.0);
122
+ mainImage(o,gl_FragCoord.xy);
123
+ fragColor=o;
124
+ }
125
+ `;
126
+
127
+ function compileShader(gl: WebGL2RenderingContext, type: number, src: string): WebGLShader {
128
+ const sh = gl.createShader(type)!;
129
+ gl.shaderSource(sh, src);
130
+ gl.compileShader(sh);
131
+ if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
132
+ const log = gl.getShaderInfoLog(sh);
133
+ gl.deleteShader(sh);
134
+ throw new Error("Shader compile failed: " + log);
135
+ }
136
+ return sh;
137
+ }
138
+
139
+ export function startGrainient(canvas: HTMLCanvasElement, opts: GrainientOptions = {}): () => void {
140
+ const o: Required<GrainientOptions> = {
141
+ timeSpeed: 0.25,
142
+ colorBalance: 0.0,
143
+ warpStrength: 1.0,
144
+ warpFrequency: 5.0,
145
+ warpSpeed: 2.0,
146
+ warpAmplitude: 10.0,
147
+ blendAngle: 0.0,
148
+ blendSoftness: 0.05,
149
+ rotationAmount: 500.0,
150
+ noiseScale: 2.0,
151
+ grainAmount: 0.1,
152
+ grainScale: 2.0,
153
+ grainAnimated: false,
154
+ contrast: 1.5,
155
+ gamma: 1.0,
156
+ saturation: 1.0,
157
+ centerX: 0.0,
158
+ centerY: 0.0,
159
+ zoom: 0.9,
160
+ color1: "#000422",
161
+ color2: "#d8dcfa",
162
+ color3: "#000422",
163
+ ...opts,
164
+ };
165
+
166
+ const gl = canvas.getContext("webgl2", { alpha: true, antialias: false, premultipliedAlpha: true });
167
+ if (!gl) {
168
+ canvas.style.display = "none";
169
+ return () => {};
170
+ }
171
+
172
+ const vs = compileShader(gl, gl.VERTEX_SHADER, GRAINIENT_VERTEX);
173
+ const fs = compileShader(gl, gl.FRAGMENT_SHADER, GRAINIENT_FRAGMENT);
174
+ const prog = gl.createProgram()!;
175
+ gl.attachShader(prog, vs);
176
+ gl.attachShader(prog, fs);
177
+ gl.linkProgram(prog);
178
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
179
+ throw new Error("Grainient link failed: " + gl.getProgramInfoLog(prog));
180
+ }
181
+
182
+ const vao = gl.createVertexArray();
183
+ gl.bindVertexArray(vao);
184
+ const buf = gl.createBuffer();
185
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
186
+ gl.bufferData(
187
+ gl.ARRAY_BUFFER,
188
+ new Float32Array([-1, -1, 3, -1, -1, 3]),
189
+ gl.STATIC_DRAW
190
+ );
191
+ const posLoc = gl.getAttribLocation(prog, "position");
192
+ gl.enableVertexAttribArray(posLoc);
193
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
194
+
195
+ gl.useProgram(prog);
196
+
197
+ const uloc = (name: string) => gl.getUniformLocation(prog, name);
198
+ const u = {
199
+ iTime: uloc("iTime"),
200
+ iResolution: uloc("iResolution"),
201
+ uTimeSpeed: uloc("uTimeSpeed"),
202
+ uColorBalance: uloc("uColorBalance"),
203
+ uWarpStrength: uloc("uWarpStrength"),
204
+ uWarpFrequency: uloc("uWarpFrequency"),
205
+ uWarpSpeed: uloc("uWarpSpeed"),
206
+ uWarpAmplitude: uloc("uWarpAmplitude"),
207
+ uBlendAngle: uloc("uBlendAngle"),
208
+ uBlendSoftness: uloc("uBlendSoftness"),
209
+ uRotationAmount: uloc("uRotationAmount"),
210
+ uNoiseScale: uloc("uNoiseScale"),
211
+ uGrainAmount: uloc("uGrainAmount"),
212
+ uGrainScale: uloc("uGrainScale"),
213
+ uGrainAnimated: uloc("uGrainAnimated"),
214
+ uContrast: uloc("uContrast"),
215
+ uGamma: uloc("uGamma"),
216
+ uSaturation: uloc("uSaturation"),
217
+ uCenterOffset: uloc("uCenterOffset"),
218
+ uZoom: uloc("uZoom"),
219
+ uColor1: uloc("uColor1"),
220
+ uColor2: uloc("uColor2"),
221
+ uColor3: uloc("uColor3"),
222
+ };
223
+
224
+ gl.uniform1f(u.uTimeSpeed, o.timeSpeed);
225
+ gl.uniform1f(u.uColorBalance, o.colorBalance);
226
+ gl.uniform1f(u.uWarpStrength, o.warpStrength);
227
+ gl.uniform1f(u.uWarpFrequency, o.warpFrequency);
228
+ gl.uniform1f(u.uWarpSpeed, o.warpSpeed);
229
+ gl.uniform1f(u.uWarpAmplitude, o.warpAmplitude);
230
+ gl.uniform1f(u.uBlendAngle, o.blendAngle);
231
+ gl.uniform1f(u.uBlendSoftness, o.blendSoftness);
232
+ gl.uniform1f(u.uRotationAmount, o.rotationAmount);
233
+ gl.uniform1f(u.uNoiseScale, o.noiseScale);
234
+ gl.uniform1f(u.uGrainAmount, o.grainAmount);
235
+ gl.uniform1f(u.uGrainScale, o.grainScale);
236
+ gl.uniform1f(u.uGrainAnimated, o.grainAnimated ? 1.0 : 0.0);
237
+ gl.uniform1f(u.uContrast, o.contrast);
238
+ gl.uniform1f(u.uGamma, o.gamma);
239
+ gl.uniform1f(u.uSaturation, o.saturation);
240
+ gl.uniform2f(u.uCenterOffset, o.centerX, o.centerY);
241
+ gl.uniform1f(u.uZoom, o.zoom);
242
+ const c1 = hexToRgb(o.color1);
243
+ const c2 = hexToRgb(o.color2);
244
+ const c3 = hexToRgb(o.color3);
245
+ gl.uniform3f(u.uColor1, c1[0], c1[1], c1[2]);
246
+ gl.uniform3f(u.uColor2, c2[0], c2[1], c2[2]);
247
+ gl.uniform3f(u.uColor3, c3[0], c3[1], c3[2]);
248
+
249
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
250
+ const setSize = () => {
251
+ const w = Math.max(1, Math.floor(canvas.clientWidth * dpr));
252
+ const h = Math.max(1, Math.floor(canvas.clientHeight * dpr));
253
+ if (canvas.width !== w || canvas.height !== h) {
254
+ canvas.width = w;
255
+ canvas.height = h;
256
+ }
257
+ gl.viewport(0, 0, w, h);
258
+ gl.uniform2f(u.iResolution, w, h);
259
+ };
260
+
261
+ const ro = new ResizeObserver(setSize);
262
+ ro.observe(canvas);
263
+ setSize();
264
+
265
+ const t0 = performance.now();
266
+ let raf = 0;
267
+ const loop = (t: number) => {
268
+ gl.uniform1f(u.iTime, (t - t0) * 0.001);
269
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
270
+ raf = requestAnimationFrame(loop);
271
+ };
272
+ raf = requestAnimationFrame(loop);
273
+
274
+ return () => {
275
+ cancelAnimationFrame(raf);
276
+ ro.disconnect();
277
+ };
278
+ }
@@ -0,0 +1,28 @@
1
+ export const glyphs = {
2
+ eyes: `
3
+ -+:
4
+ :=- -%@@@%.
5
+ *@@@@@#- *@@-
6
+ +@@. +@
7
+ @@= -#=-+++=+:
8
+ #% .:===-: -@* +@@@@%
9
+ *@-+@@@@@: %@@+ @@@=#@
10
+ *@= @@@@@@@- .@.@@@@@@@ :
11
+ @@+=@@@@@@@@@@@@: .% *@@@@@*-=
12
+ #:-@ -@@@@@@@@@-+% @ -@@@- #
13
+ : #+ @@@@@@@- -% =# =
14
+ -@: *@ .+%%
15
+ :%#: --
16
+ .-:
17
+ `,
18
+ ellipsis: "…",
19
+ };
20
+
21
+ export function escapeHtml(value: unknown): string {
22
+ return String(value == null ? "" : value)
23
+ .replaceAll("&", "&amp;")
24
+ .replaceAll("<", "&lt;")
25
+ .replaceAll(">", "&gt;")
26
+ .replaceAll("\"", "&quot;");
27
+ }
28
+ export function renderEyes(): string { return escapeHtml(glyphs.eyes); }