@markee_team/sdk 0.0.0-stage → 0.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Markee contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,183 @@
1
- # Temporary Holding Version
1
+ # @markee_team/sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An ESM JavaScript SDK with a self-contained, Shadow DOM modal and an optional React entry point. The SDK knows the Markee API endpoint; the host only supplies its placement and, optionally, its current wallet provider. No Alchemy, Redis, Privy, or bridge-provider key belongs in the consuming app.
4
+
5
+ This is an experimental `0.1.0-alpha.2` release for wallet-funded For Sale and For Rent flows. Hosted card checkout and incoming cross-chain funding are not included.
6
+
7
+ Install the alpha:
8
+
9
+ ```sh
10
+ npm install @markee_team/sdk@alpha
11
+ ```
12
+
13
+ Use React 18 or 19 for the optional React entry point. The plain JavaScript entry point does not require React. Both TypeScript bundler and NodeNext resolution are supported.
14
+
15
+ To build from the source repository:
16
+
17
+ ```sh
18
+ npm ci --prefix packages/sdk
19
+ npm run build --prefix packages/sdk
20
+ ```
21
+
22
+ ## Plain JavaScript
23
+
24
+ ```ts
25
+ import { createMarkee } from '@markee_team/sdk';
26
+
27
+ const markee = createMarkee({
28
+ // Optional. Reuse the wallet selected by your application's wallet connector.
29
+ wallet: {
30
+ getProvider: async () => appWalletProvider ?? null,
31
+ connect: async () => connectAppWallet(),
32
+ },
33
+ onEvent: event => console.log(event.type, event.hash),
34
+ });
35
+
36
+ markee.open({
37
+ placement: {
38
+ kind: 'board',
39
+ chainId: 8453,
40
+ address: '0x2768BC6e90266248BD8bCF5401C36D8049CdF671',
41
+ },
42
+ });
43
+
44
+ // markee.close();
45
+ ```
46
+
47
+ Without a wallet adapter, browsing works without a wallet. On a connect click the modal discovers installed wallets using EIP-6963, with an EIP-1193 fallback. Multiple discovered wallets are offered for explicit selection. An adapter always takes precedence over injected discovery. Hosted login/card checkout is not part of milestones 1–4.
48
+
49
+ ## React
50
+
51
+ ```tsx
52
+ import { MarkeeModal } from '@markee_team/sdk/react';
53
+
54
+ <MarkeeModal
55
+ open={open}
56
+ onClose={() => setOpen(false)}
57
+ wallet={walletAdapter}
58
+ placement={{ kind: 'gardens-community', chainId: 100, community: communityAddress }}
59
+ />;
60
+ ```
61
+
62
+ See [`examples/connectkit`](https://github.com/1Hive/markee/tree/feature/markee-sdk/examples/connectkit) for a complete adapter. Its connection promise resolves only once ConnectKit has selected a provider and rejects when the picker is dismissed. The SDK does not install a second wallet context.
63
+
64
+ ## Modal layout
65
+
66
+ Both strategies follow Gardens' For Rent modal: a compact, 512px panel with a fixed header/footer, a collapsible leaderboard, the selected message or editor, and a prominent amount card. The leaderboard pins your loaded messages and offers inline funding selection. Your active rental message is selected when available; otherwise an owned message or the creation form is shown.
67
+
68
+ For Sale uses the same arrangement with a one-time ETH amount. For Rent uses an ETHx monthly rate and links to Deposit manager. Wallet balances, rewards, refunds, edits, and community treasury actions remain accessible without top-level tabs. The footer holds the review/confirmation actions and Stop streaming when applicable.
69
+
70
+ MIN and WIN/2× fill the amount without sending a payment. Suggestions use the loaded ranking, preserve integer precision, and account for the selected message's existing funding or stream contribution. Rankings can change; the server still validates and simulates each reviewed action. New rentals retain the selected rate across message creation and require a separate stream confirmation. Payment reviews and drafts survive navigation back to the editor.
71
+
72
+ ## Theme
73
+
74
+ Pass a typed `theme` option to `createMarkee` or a `theme` prop to the React modal. Light is the default; `mode: 'dark'` selects the dark palette and `mode: 'system'` follows device changes while the modal is open.
75
+
76
+ ```tsx
77
+ import { createMarkee, type MarkeeTheme } from '@markee_team/sdk';
78
+
79
+ const theme: MarkeeTheme = {
80
+ mode: 'system',
81
+ fontFamily: '"DM Sans", system-ui, sans-serif',
82
+ radius: 12,
83
+ colors: {
84
+ primary: '#743a84',
85
+ primaryText: '#ffffff',
86
+ accent: '#eac6f1',
87
+ accentText: '#50265b',
88
+ },
89
+ };
90
+
91
+ // React: <MarkeeModal {...modalProps} theme={theme} />
92
+ // JavaScript:
93
+ const markee = createMarkee({ theme });
94
+ markee.setTheme({ mode: 'dark' });
95
+ markee.setTheme(); // Reset to the default light theme.
96
+ ```
97
+
98
+ Theme changes update styles in place: drafts, focus, scroll, wallet sessions, and payment reviews stay intact. In JavaScript, `setTheme` replaces the theme for the open modal and future opens. You can also supply `open({ placement, theme })` for a single opening, or call `modal.setTheme(theme)` on the returned modal. In React, update the `theme` prop. Removing it resets the default.
99
+
100
+ | Option | Customization |
101
+ | --- | --- |
102
+ | `mode` | `light`, `dark`, or `system` |
103
+ | `fontFamily` | CSS font stack; the app supplies any custom font |
104
+ | `radius` | Panel radius in pixels, 0–48 (default 22); card and control corners scale with it |
105
+ | `colors` | Partial overrides for the semantic tokens below |
106
+
107
+ Color tokens: `background`, `surface`, `text`, `muted`, `border`, `inputBackground`, `inputText`, `primary`, `primaryText`, `accent`, `accentText`, `accentSurface`, `accentSurfaceText`, `accentBorder`, `selected`, `selectedText`, `info`, `infoText`, `error`, `errorText`, `success`, `successText`, `link`, `focusRing`, and `overlay`. Set foreground/background pairs together and check your brand's contrast in both modes. Overrides apply over either selected palette; omitted values use that palette's defaults. Invalid CSS color/font values fall back to defaults.
108
+
109
+ Styles are scoped to the modal's Shadow DOM and do not change the host app or its wallet picker. The ConnectKit sample includes light, dark, device, and custom Plum presets.
110
+
111
+ ## Headless API
112
+
113
+ `new MarkeeClient(options)` provides `snapshot`, `prepare`, `send`, `verify`, `pending`, `recordView`, `quote`, `authorize`, `executeAuthorization`, `resumeAuthorization`, and `bridgeStatus`. Exported TypeScript types describe the complete request/response contract.
114
+
115
+ - `snapshot(placement, account?, offset?)`: chain state, metadata, moderation, views, rewards and wallet balances. The first page is ranked; subsequent pages browse the registry. Enforced winner and the wallet's current backing remain included.
116
+ - `prepare(placement, action)`: validates membership/ownership, simulates and estimates the exact transaction, and returns a five-minute review. All financial inputs are decimal **wei strings**; use `parseEth` for user input.
117
+ - `send(preparation)`: rechecks account, requests a chain switch when necessary, and sends through the host wallet. It never signs with a backend key.
118
+ - `verify(pending)`: binds the submitted hash to the prepared sender, recipient, calldata and value, waits for two block confirmations, and reports actual token transfers. A reverted transaction or deferred settlement is not a reward success.
119
+ - Pending transactions are stored per API URL, wallet and placement. Closing/reopening resumes verification. Unknown transactions remain pending and block duplicate submissions. A replacement hash is not automatically inferred; inspect it in the wallet/explorer and pass the matching replacement to `verify`.
120
+ - Admin signatures have a five-minute challenge window, one-time nonces, and a durable server-side operation record. Retrying the same signature recovers the saved operation instead of spending twice. In-flight signatures are stored locally for recovery and removed after a terminal result. A record interrupted before a hash was saved requires operator reconciliation; it is never automatically executed again.
121
+
122
+ ## Payment semantics
123
+
124
+ For Sale supports create/fund/edit. New message ownership belongs to the caller. Funding an existing message does not transfer ownership; MARKEE rewards go to the payer when RevNet is enabled.
125
+
126
+ For Rent supports message creation, starting/updating/switching/stopping a stream, wrap/unwrap, pool connection/refunds, board-buffer recovery, ranking refresh and settlement. The server builds the complete Superfluid batch, including exact ETHx approval and pool connection. Wallet runway uses the combined CFA and GDA net flow across the wallet, not only the current board. Rental MARKEE comes from actual settled rent, never merely from wrapping or depositing ETH.
127
+
128
+ Community activation supports the current council Safe, a current Safe owner, or the configured authorized keeper. Revenue claims support the current Safe or its current owners and always pay the council treasury. Existing outbound Across, Squid and LI.FI routing lives on Markee's server.
129
+
130
+ ## Integration boundaries
131
+
132
+ - Current board versions: `1.3.0` For Sale and `streaming-1.0.0` For Rent, on Base and configured Sepolia deployments. Additional factories must be enabled on Markee's server. Legacy strategy versions are rejected explicitly.
133
+ - Transaction sending currently expects an EIP-1193 wallet returning an on-chain transaction hash with the connected wallet as the transaction sender. UserOperation-only and Safe transaction-service hashes need a future adapter; they are not treated as successful direct transactions. Community authorization independently supports ERC-1271 Safe signatures.
134
+ - The native ETHx downgrade uses the deployed contract's native transfer path. Contract accounts whose receive function needs more than its transfer stipend may not support it; simulation catches the failure before prompting.
135
+ - The modal renders escaped text, uses isolated CSS, and supports keyboard focus, Escape, mobile sizing and reduced motion. A host CSP must permit the SDK's inline styles and connections to its configured Markee API. No external font or image is needed by the modal.
136
+ - API failures are explicit. Without moderation data, message text is withheld. No mock balances or fabricated rewards are returned by production handlers.
137
+
138
+ Backend configuration, endpoint details, and verification evidence: [`docs/sdk/implementation.md`](https://github.com/1Hive/markee/blob/feature/markee-sdk/docs/sdk/implementation.md).
139
+
140
+ ## API endpoint
141
+
142
+ No API URL configuration is required. This alpha uses the Markee staging API at `https://markee-git-feature-markee-sdk-1hive.vercel.app/api/sdk/v1`. For local development or testing only, pass an optional `apiUrl` override to `createMarkee`, `MarkeeClient`, or the React modal. The current default is also exported as `DEFAULT_MARKEE_API_URL`.
143
+
144
+ ## LLM implementation prompt
145
+
146
+ Copy this prompt into your coding assistant. These are existing Base demo boards; replace the placements with your own when integrating your product.
147
+
148
+ ```text
149
+ Integrate @markee_team/sdk@0.1.0-alpha.2 into this app. Inspect the app first and
150
+ follow its existing framework, styling, and wallet patterns.
151
+
152
+ - Install @markee_team/sdk@0.1.0-alpha.2. The SDK knows its API endpoint. Do not
153
+ request an API URL, build an API proxy, or add RPC/database/provider keys.
154
+ - For React 18/19, import MarkeeModal from @markee_team/sdk/react in a client
155
+ component and supply open, onClose, and placement. For other apps, import
156
+ createMarkee from @markee_team/sdk, call createMarkee(), then open({ placement }).
157
+ Close the instance on teardown. Reuse the built-in modal UI.
158
+ - Add buttons for these existing demo placements:
159
+ For Sale: { kind: 'board', chainId: 8453,
160
+ address: '0x2768BC6e90266248BD8bCF5401C36D8049CdF671' }
161
+ For Rent: { kind: 'board', chainId: 8453,
162
+ address: '0x82cbe3BdFC4e01940D186Ac5EA151355647DE0d3' }
163
+ - Wallet integration is optional. Reuse the app's selected EIP-1193 provider via
164
+ wallet.getProvider(): Promise<provider | null> and optional
165
+ wallet.connect(): Promise<provider>. For ConnectKit/wagmi, use the active
166
+ connector's provider, resolve connect after selection, and reject cancellation.
167
+ Do not create a second wallet context or silently select another injected wallet.
168
+ If there is no existing wallet integration, omit wallet for SDK wallet discovery.
169
+ - Match the app using theme: mode ('light', 'dark', 'system'), fontFamily, radius,
170
+ and semantic colors such as primary and primaryText. Preserve the built-in
171
+ layout and its isolated Shadow DOM styles.
172
+ - Optionally handle onEvent and show success only for a confirmed event. Let the
173
+ SDK handle payment review, chain switching, submission, and verification.
174
+ Do not advertise card checkout or incoming bridging: this alpha lacks those
175
+ features. The demo boards use real Base funds.
176
+ - Run typecheck/build and verify both modals load without API configuration,
177
+ open/close correctly, and match the theme. Verify host wallet reuse if available.
178
+ Do not sign or submit transactions during automated verification. Report changed
179
+ files, checks, and remaining limitations.
180
+
181
+ This alpha automatically uses Markee's temporary staging backend. Read the installed
182
+ package README for exact types and configuration details.
183
+ ```
@@ -0,0 +1,5 @@
1
+ /** No floating point conversion when preparing financial amounts. */
2
+ export declare function parseEth(input: string, allowZero?: boolean): string;
3
+ export declare function formatEth(raw: string, decimals?: number): string;
4
+ export declare function shortAddress(value: string): string;
5
+ export declare function escapeHtml(value: unknown): string;
@@ -0,0 +1,71 @@
1
+ import type { Action, Address, AdminOperation, AdminResult, Authorization, BridgeStatus, ClaimQuote, EIP1193Provider, Hex, Placement, PreparedAction, SDKOptions, Snapshot, TransactionResult } from './types.js';
2
+ /** Markee-hosted API for the alpha release. */
3
+ export declare const DEFAULT_MARKEE_API_URL = "https://markee-git-feature-markee-sdk-1hive.vercel.app/api/sdk/v1";
4
+ export declare class MarkeeError extends Error {
5
+ code: string;
6
+ status?: number | undefined;
7
+ constructor(message: string, code?: string, status?: number | undefined);
8
+ }
9
+ export interface DiscoveredWallet {
10
+ name: string;
11
+ uuid: string;
12
+ provider: EIP1193Provider;
13
+ }
14
+ export declare function discoverWallets(): Promise<DiscoveredWallet[]>;
15
+ export interface PendingTransaction {
16
+ preparation: PreparedAction;
17
+ hash: Hex;
18
+ }
19
+ export interface PendingAuthorization {
20
+ kind: 'activate' | 'claim';
21
+ nonce: string;
22
+ signature: Hex;
23
+ account: Address;
24
+ community: Address;
25
+ chainId: number;
26
+ }
27
+ export declare class MarkeeClient {
28
+ readonly options: SDKOptions;
29
+ readonly apiUrl: string;
30
+ private readonly fetcher;
31
+ private injected;
32
+ private sending;
33
+ private inMemoryPending;
34
+ private adminMemory;
35
+ private communityReceipts;
36
+ constructor(options?: SDKOptions);
37
+ request<T>(path: string, body?: unknown, signal?: AbortSignal): Promise<T>;
38
+ provider(): Promise<EIP1193Provider | null>;
39
+ account(): Promise<Address | null>;
40
+ connect(selected?: EIP1193Provider): Promise<Address>;
41
+ snapshot(placement: Placement, account?: Address | null, offset?: number, signal?: AbortSignal): Promise<Snapshot>;
42
+ prepare(placement: Placement, action: Action): Promise<PreparedAction>;
43
+ private ensureWallet;
44
+ send(preparation: PreparedAction): Promise<Hex>;
45
+ verify(pending: PendingTransaction): Promise<TransactionResult>;
46
+ /** Verify a speed-up replacement against the original prepared calldata before
47
+ * retiring the old hash. Unknown/replaced transactions never authorize a retry. */
48
+ reconcile(pending: PendingTransaction, replacementHash: Hex): Promise<TransactionResult>;
49
+ private storageKey;
50
+ private records;
51
+ private persist;
52
+ pending(placement: Placement, account: Address): PendingTransaction | undefined;
53
+ quote(placement: Extract<Placement, {
54
+ kind: 'gardens-community';
55
+ }>): Promise<ClaimQuote>;
56
+ authorize(placement: Extract<Placement, {
57
+ kind: 'gardens-community';
58
+ }>, kind: 'activate' | 'claim'): Promise<Authorization>;
59
+ executeAuthorization(kind: 'activate' | 'claim', authorization: Authorization, account: Address): Promise<AdminOperation>;
60
+ pendingAuthorization(): PendingAuthorization | null;
61
+ private saveAuthorization;
62
+ resumeAuthorization(): Promise<AdminOperation>;
63
+ communityReceipt(placement: Extract<Placement, {
64
+ kind: 'gardens-community';
65
+ }>): AdminResult | null;
66
+ bridgeStatus(protocol: ClaimQuote['bridgeProtocol'], hash: Hex, fromChainId: number, toChainId: number): Promise<BridgeStatus>;
67
+ recordView(address: Address, message: string, url?: string): Promise<{
68
+ counted: boolean;
69
+ totalViews: number;
70
+ }>;
71
+ }
@@ -0,0 +1,6 @@
1
+ export * from './types.js';
2
+ export { MarkeeClient, MarkeeError, discoverWallets, DEFAULT_MARKEE_API_URL } from './client.js';
3
+ export type { PendingTransaction, DiscoveredWallet } from './client.js';
4
+ export { createMarkee, MarkeeModal } from './modal.js';
5
+ export type { ModalOptions } from './modal.js';
6
+ export { parseEth, formatEth } from './amount.js';
package/dist/index.js ADDED
@@ -0,0 +1,107 @@
1
+ var H="https://markee-git-feature-markee-sdk-1hive.vercel.app/api/sdk/v1",p=class extends Error{constructor(t,a="MARKEE_ERROR",i){super(t);this.code=a;this.status=i;this.name="MarkeeError"}};async function E(){if(typeof window>"u")return[];let s=new Map,e=a=>{let i=a.detail;i?.provider?.request&&typeof i.info?.uuid=="string"&&s.set(i.info.uuid,{uuid:i.info.uuid,name:String(i.info.name).slice(0,80),provider:i.provider})};window.addEventListener("eip6963:announceProvider",e),window.dispatchEvent(new Event("eip6963:requestProvider")),await new Promise(a=>setTimeout(a,150)),window.removeEventListener("eip6963:announceProvider",e);let t=window.ethereum;return!s.size&&t?.request&&s.set("injected",{uuid:"injected",name:"Browser wallet",provider:t}),[...s.values()]}var x=s=>typeof s=="string"&&/^0x[\da-f]{40}$/i.test(s),T=s=>typeof s=="string"&&/^0x[\da-f]{64}$/i.test(s),k=class{constructor(e={}){this.options=e;let t=new URL(e.apiUrl??H);if(t.protocol!=="https:"&&!(t.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)))throw new p("Use an HTTPS Markee API URL (HTTP is allowed on localhost).");this.apiUrl=t.href.replace(/\/$/,""),this.fetcher=e.fetch||globalThis.fetch.bind(globalThis)}apiUrl;fetcher;injected=null;sending=!1;inMemoryPending=[];adminMemory=null;communityReceipts={};async request(e,t,a){let i=await this.fetcher(`${this.apiUrl}/${e}`,{method:t===void 0?"GET":"POST",body:t===void 0?void 0:JSON.stringify(t),headers:t===void 0?void 0:{"Content-Type":"application/json"},credentials:"omit",signal:a||AbortSignal.timeout(55e3)}),r=await i.json().catch(()=>null);if(!i.ok)throw new p(r?.error||`Markee returned HTTP ${i.status}.`,r?.code,i.status);return r}async provider(){return this.options.wallet?this.options.wallet.getProvider():this.injected}async account(){let e=await this.provider();if(!e)return null;let t=await e.request({method:"eth_accounts"});return Array.isArray(t)&&x(t[0])?t[0]:null}async connect(e){let t=await this.provider();if(this.options.wallet)t||(t=await this.options.wallet.connect?.()||null);else{if(t=e||t,!t){let i=await E();if(i.length>1)throw new p("Select a browser wallet.","SELECT_WALLET");t=i[0]?.provider||null}this.injected=t}if(!t)throw new p("Connect a wallet in the app or install a browser wallet to continue.","NO_WALLET");let a=await t.request({method:"eth_requestAccounts"});if(!Array.isArray(a)||!x(a[0]))throw new p("The wallet did not return an account.");return a[0]}async snapshot(e,t,a,i){let r=new URLSearchParams({placement:JSON.stringify(e)});return t&&r.set("account",t),a!==void 0&&r.set("offset",String(a)),this.request(`snapshot?${r}`,void 0,i)}async prepare(e,t){let a=await this.account();if(!a)throw new p("Connect your wallet first.","NOT_CONNECTED");return this.request("prepare",{placement:e,account:a,action:t})}async ensureWallet(e,t){let a=await this.provider();if(!a||(await this.account())?.toLowerCase()!==e.toLowerCase())throw new p("Your wallet account changed. Review this action again.","ACCOUNT_CHANGED");let i=Number(await a.request({method:"eth_chainId"}));if(i!==t&&await a.request({method:"wallet_switchEthereumChain",params:[{chainId:`0x${t.toString(16)}`}]}),i=Number(await a.request({method:"eth_chainId"})),i!==t||(await this.account())?.toLowerCase()!==e.toLowerCase())throw new p("The wallet account or network changed. Review again.","WALLET_CHANGED");return a}async send(e){if(this.sending)throw new p("A wallet request is already in progress.");if(e.expiresAt<=Date.now())throw new p("This review expired. Prepare the action again.","REVIEW_EXPIRED");if(this.pending(e.placement,e.transaction.from))throw new p("A transaction is still pending for this wallet and placement. Check it before submitting again.","PENDING_TRANSACTION");this.sending=!0;try{let a=await this.ensureWallet(e.transaction.from,e.transaction.chainId);if(e.expiresAt<=Date.now())throw new p("This review expired. Prepare the action again.","REVIEW_EXPIRED");let{chainId:i,...r}=e.transaction,n=await a.request({method:"eth_sendTransaction",params:[r]});if(!T(n))throw new p("The wallet returned an unexpected transaction identifier. Check your wallet before retrying.");return this.persist([...this.records(),{preparation:e,hash:n}]),this.options.onEvent?.({type:"submitted",hash:n}),n}catch(a){throw this.options.onEvent?.({type:"error",message:a instanceof Error?a.message:String(a)}),a}finally{this.sending=!1}}async verify(e){let t=await this.request("verify",{id:e.preparation.id,hash:e.hash});return t.status!=="pending"&&(this.persist(this.records().filter(a=>a.hash!==e.hash)),t.status==="confirmed"&&this.options.onEvent?.({type:"confirmed",hash:t.hash})),t}async reconcile(e,t){if(!T(t))throw new p("Enter a valid replacement transaction hash.");let a=await this.request("verify",{id:e.preparation.id,hash:t});return a.status!=="pending"&&this.persist(this.records().filter(i=>i.hash!==e.hash)),a}storageKey(){return`markee:sdk:v1:pending:${this.apiUrl}`}records(){try{if(typeof localStorage>"u")return this.inMemoryPending;let e=JSON.parse(localStorage.getItem(this.storageKey())||"[]");return Array.isArray(e)?e.filter(t=>T(t?.hash)&&typeof t.preparation?.id=="string"&&x(t.preparation?.transaction?.from)&&t.preparation?.placement):[]}catch{return this.inMemoryPending}}persist(e){this.inMemoryPending=e;try{typeof localStorage<"u"&&localStorage.setItem(this.storageKey(),JSON.stringify(e))}catch{}}pending(e,t){return this.records().find(a=>JSON.stringify(a.preparation.placement).toLowerCase()===JSON.stringify(e).toLowerCase()&&a.preparation.transaction.from.toLowerCase()===t.toLowerCase())}async quote(e){return this.request(`community/quote?${new URLSearchParams({chainId:String(e.chainId),community:e.community})}`)}async authorize(e,t){let a=await this.account();if(!a)throw new p("Connect your community council wallet first.");return this.request(`community/${t}`,{action:"challenge",chainId:e.chainId,community:e.community,account:a})}async executeAuthorization(e,t,a){if(this.pendingAuthorization())throw new p("An earlier community authorization is still processing. Check its status first.");let i=t.typedData,n=await(await this.ensureWallet(a,i.domain.chainId)).request({method:"eth_signTypedData_v4",params:[a,JSON.stringify({...i,types:{...i.types,EIP712Domain:[{name:"name",type:"string"},{name:"version",type:"string"},{name:"chainId",type:"uint256"},{name:"verifyingContract",type:"address"}]}})]});if(typeof n!="string"||!/^0x[\da-f]+$/i.test(n))throw new p("Wallet did not return a typed-data signature.");return this.saveAuthorization({kind:e,nonce:t.nonce,signature:n,account:a,community:i.domain.verifyingContract,chainId:i.domain.chainId}),this.resumeAuthorization()}pendingAuthorization(){try{let e=typeof localStorage>"u"?this.adminMemory:JSON.parse(localStorage.getItem(`${this.storageKey()}:admin`)||"null");return e&&["activate","claim"].includes(e.kind)&&x(e.account)&&x(e.community)&&typeof e.nonce=="string"&&typeof e.signature=="string"?e:null}catch{return this.adminMemory}}saveAuthorization(e){this.adminMemory=e;try{typeof localStorage<"u"&&localStorage.setItem(`${this.storageKey()}:admin`,JSON.stringify(e))}catch{}}async resumeAuthorization(){let e=this.pendingAuthorization();if(!e)throw new p("There is no pending community authorization.");let t=await this.request(`community/${e.kind}`,{action:"verify",nonce:e.nonce,signature:e.signature});if(t.status==="complete"&&t.result){let a=`${e.chainId}:${e.community.toLowerCase()}`;this.communityReceipts[a]=t.result;try{typeof localStorage<"u"&&localStorage.setItem(`${this.storageKey()}:community:${a}`,JSON.stringify(t.result))}catch{}}return(t.status==="complete"||t.status==="failed")&&this.saveAuthorization(null),t}communityReceipt(e){let t=`${e.chainId}:${e.community.toLowerCase()}`;try{let a=typeof localStorage>"u"?this.communityReceipts[t]:JSON.parse(localStorage.getItem(`${this.storageKey()}:community:${t}`)||"null");return a&&T(a.transactionHash)?a:null}catch{return this.communityReceipts[t]||null}}bridgeStatus(e,t,a,i){return this.request(`community/status?${new URLSearchParams({protocol:e,transactionHash:t,fromChainId:String(a),toChainId:String(i)})}`)}recordView(e,t,a){return this.request("views",{address:e,message:t,url:a})}};function g(s,e=!1){let t=s.trim();if(!/^\d+(\.\d{0,18})?$/.test(t))throw new Error("Enter an ETH amount with up to 18 decimal places.");let[a,i=""]=t.split("."),r=BigInt(a)*10n**18n+BigInt(i.padEnd(18,"0"));if(!e&&r===0n||r>=2n**256n)throw new Error("Enter a positive amount.");return r.toString()}function m(s,e=6){let t=BigInt(s),a=t<0n?"-":"",i=t<0n?-t:t,r=(i%10n**18n).toString().padStart(18,"0").slice(0,e).replace(/0+$/,"");return i>0n&&i<10n**BigInt(18-e)?`${a}<0.${"0".repeat(e-1)}1`:`${a}${i/10n**18n}${r?`.${r}`:""}`}function C(s){return`${s.slice(0,6)}\u2026${s.slice(-4)}`}function l(s){return String(s).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e])}var R=`
2
+ :host{all:initial;font-family:var(--markee-font-family);color:var(--markee-text);font-size:14px;line-height:1.5;color-scheme:var(--markee-color-scheme)}
3
+
4
+ *{box-sizing:border-box}
5
+ button,input,textarea,select{font:inherit}
6
+ button{cursor:pointer}
7
+ button:disabled{opacity:.5;cursor:wait}
8
+ button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,a:focus-visible{outline:3px solid var(--markee-focus-ring);outline-offset:3px}
9
+
10
+ .overlay{position:fixed;inset:0;z-index:10000;background:var(--markee-overlay);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;padding:24px}
11
+
12
+ .panel{background:var(--markee-background);border:1px solid var(--markee-border);border-radius:var(--markee-radius);width:min(512px,100%);max-height:92dvh;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 24px 100px #10183244;position:relative}
13
+
14
+ header{flex-shrink:0;padding:18px 24px;display:flex;gap:16px;align-items:flex-start;border-bottom:1px solid var(--markee-border)}
15
+ .brand{width:40px;height:40px;display:grid;place-items:center;border-radius:var(--markee-control-radius);background:var(--markee-primary);color:var(--markee-primary-text);font-weight:900;font-size:24px}
16
+ .grow{flex:1;min-width:0}
17
+ h1{font-size:18px;line-height:1.25;margin:0 0 4px}
18
+ h2{font-size:18px;line-height:1.3;margin:0 0 12px}
19
+ h3{font-size:16px;margin:0 0 8px}
20
+ p{margin:8px 0 14px}
21
+ .muted,small{color:var(--markee-muted)}
22
+ .eyebrow{font-size:11px;text-transform:uppercase;letter-spacing:1.3px;font-weight:700;color:var(--markee-accent-surface-text)}
23
+ .pill{display:inline-flex;border-radius:var(--markee-small-radius);background:var(--markee-accent-surface);color:var(--markee-accent-surface-text);padding:3px 7px;font-size:11px;font-weight:700}
24
+ .close{border:0;background:none;color:var(--markee-muted);font-size:23px;padding:5px 10px;border-radius:var(--markee-small-radius)}
25
+ .nav{display:flex;gap:4px;padding:12px 24px 0;overflow:auto}
26
+ .nav button{white-space:nowrap;border:0;background:transparent;padding:10px 12px;color:var(--markee-muted);border-radius:var(--markee-control-radius);font-size:13px}
27
+ .nav button[aria-selected=true]{background:var(--markee-selected);color:var(--markee-selected-text);font-weight:650}
28
+ .content{padding:20px 24px;overflow-y:auto;overflow-x:hidden;min-height:0;overscroll-behavior:contain}
29
+ .row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
30
+ .between{justify-content:space-between}
31
+ .stack{display:grid;gap:12px}
32
+ .btn{border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);background:var(--markee-surface);color:var(--markee-text);padding:10px 15px;font-weight:600;font-size:13px}
33
+ .primary{background:var(--markee-primary);border-color:var(--markee-primary);color:var(--markee-primary-text)}
34
+ .accent{background:var(--markee-accent);border-color:var(--markee-accent);color:var(--markee-accent-text)}
35
+ .danger{color:var(--markee-error-text)}
36
+ .full{width:100%}
37
+ .card{border:1px solid var(--markee-border);border-radius:var(--markee-card-radius);background:var(--markee-surface);padding:17px 18px}
38
+ .winner{background:var(--markee-accent-surface);border-color:var(--markee-accent-border)}
39
+ .message{font-family:ui-monospace,monospace;font-size:15px;overflow-wrap:anywhere;white-space:pre-wrap;line-height:1.5;margin:10px 0}
40
+ .name{font-size:12px;color:var(--markee-muted)}
41
+ .meta{font-size:12px;color:var(--markee-muted)}
42
+ .stat{display:flex;justify-content:space-between;gap:16px;margin:8px 0;font-size:13px}
43
+ .stat strong{text-align:right;overflow-wrap:anywhere}
44
+ .grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
45
+ .metric{font-size:23px;font-weight:650;letter-spacing:-.6px}
46
+ .note,.error,.success{padding:12px 14px;border-radius:var(--markee-control-radius);font-size:13px;overflow-wrap:anywhere;margin-bottom:14px}
47
+ .note{background:var(--markee-info);color:var(--markee-info-text)}
48
+ .error{background:var(--markee-error);color:var(--markee-error-text)}
49
+ .success{background:var(--markee-success);color:var(--markee-success-text)}
50
+ label{display:block;font-size:13px;font-weight:600;margin-bottom:6px}
51
+ input,textarea,select{display:block;width:100%;border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);background:var(--markee-input-background);color:var(--markee-input-text);padding:12px;min-width:0}
52
+ input::placeholder,textarea::placeholder{color:var(--markee-muted);opacity:1}
53
+ textarea{font-family:ui-monospace,monospace;font-size:14px;min-height:88px;max-height:220px;resize:vertical}
54
+ form{display:grid;gap:16px}
55
+ .field small{display:block;margin-top:5px;font-weight:400;font-size:12px}
56
+ .divider{height:1px;background:var(--markee-border);margin:20px 0}
57
+ a{color:var(--markee-link);text-decoration:none;overflow-wrap:anywhere}
58
+ ul{padding-left:20px;font-size:14px}
59
+ .review li{margin-bottom:9px;overflow-wrap:anywhere}
60
+ .loading{padding:40px;text-align:center;color:var(--markee-muted)}
61
+ .empty{padding:24px 8px;text-align:center}
62
+ .spinner{display:inline-block;width:14px;height:14px;border-radius:100%;border:2px solid var(--markee-border);border-top-color:var(--markee-muted);animation:spin 1s linear infinite;margin-right:8px}
63
+ @keyframes spin{to{transform:rotate(360deg)}
64
+ }
65
+ footer{flex-shrink:0;padding:14px 20px;border-top:1px solid var(--markee-border);background:var(--markee-background);color:var(--markee-muted);font-size:11px}
66
+ .mono{font-family:ui-monospace,monospace}
67
+ .review{margin:16px 0}.review ul{padding-left:18px}.review .stat{border-top:1px solid var(--markee-border);padding-top:10px}.review-title{font-size:14px;color:var(--markee-muted);font-weight:500}
68
+ .wallets{display:grid;gap:10px}
69
+
70
+
71
+ .header-caption{font-size:12px;color:var(--markee-muted);margin:5px 0 0}
72
+ .close{line-height:1;padding:4px 6px;flex-shrink:0}
73
+ .text-button{border:0;background:transparent;color:var(--markee-link);padding:0;font-size:12px;font-weight:500;text-align:left}
74
+ .compact{padding:5px 9px;font-size:11px;line-height:1.4}
75
+ .leaderboard{border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);padding:12px 14px;background:var(--markee-surface);margin-bottom:20px}
76
+ .leaderboard>.row{gap:8px}.leaderboard a{font-size:11px}.leaderboard .text-button{color:var(--markee-muted)}.chevron{display:inline-block;width:15px;font-size:16px}
77
+ .leaderboard-list{display:grid;gap:10px;margin-top:12px;padding-top:12px;border-top:1px solid var(--markee-border)}
78
+ .leaderboard-entry{display:flex;gap:12px;align-items:center;justify-content:space-between;border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);padding:10px}
79
+ .selected-entry{border-color:var(--markee-link)}.leaderboard-entry .message{font-size:12px;line-height:1.5;margin:0}.entry-caption{gap:5px;margin-top:5px}.entry-caption:empty{display:none}.entry-caption .pill{font-size:9px}.entry-caption .name,.entry-caption .meta{font-size:10px}
80
+ .entry-funding{display:flex;flex-direction:column;align-items:flex-end;gap:4px;flex:0 0 auto;max-width:44%;text-align:right;overflow-wrap:anywhere}.entry-funding .meta{font-size:10px}
81
+ .message-card{padding:16px;border:1px solid var(--markee-border);border-radius:var(--markee-card-radius);background:var(--markee-surface);min-width:0}
82
+ .message-card.winner{background:var(--markee-accent-surface);border-color:var(--markee-accent-border)}
83
+ .message-card>.message{font-size:17px;margin:12px 0 6px}.message-card .eyebrow{font-size:10px}.message-card>.name{margin:4px 0}
84
+ .message-tools{margin-top:12px;gap:10px 16px}.message-tools .text-button{font-size:11px}.editor-card{display:grid;gap:16px}
85
+ .field label{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.field-count{font-size:10px;font-weight:400;color:var(--markee-muted)}.field small{font-size:10px}
86
+ .message-stats{border-top:1px solid var(--markee-border);margin-top:16px;padding-top:12px}.message-stats>.meta{font-size:11px}.stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:8px}.stats-grid>div{min-width:0}.stats-grid strong{display:block;font-family:ui-monospace,monospace;font-size:12px;overflow-wrap:anywhere}.stat-label{display:block;font-size:9px;letter-spacing:.6px;text-transform:uppercase;color:var(--markee-muted);margin-bottom:4px}
87
+ .funding-card{border:1px solid var(--markee-link);border-radius:var(--markee-card-radius);background:var(--markee-surface);padding:16px;min-width:0}
88
+ .funding-card>label{font-size:11px;color:var(--markee-muted);font-weight:500;margin-bottom:8px}.amount-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.amount-value{display:flex;gap:6px;align-items:baseline;min-width:0;flex:1 1 140px}.amount-value input{background:transparent;border:0;border-radius:2px;box-shadow:none;font-family:ui-monospace,monospace;font-size:23px;font-weight:600;min-width:0;width:100%;padding:3px 0;color:var(--markee-text)}.amount-value>span{font-size:11px;color:var(--markee-muted);white-space:nowrap}.amount-presets{display:flex;gap:6px;margin-left:auto}.amount-presets button{color:var(--markee-link)}.amount-presets button[aria-pressed=true]{border-color:var(--markee-link);background:var(--markee-selected);color:var(--markee-selected-text)}
89
+ .funding-caption{display:flex;justify-content:space-between;flex-wrap:wrap;gap:4px 10px;margin-top:9px;color:var(--markee-muted);font-size:10px}.funding-tools{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;border-top:1px solid var(--markee-border);margin-top:13px;padding-top:12px}.funding-tools .meta{font-size:10px}.funding-hint{font-size:11px;margin:0}.funding-hint+.note{margin:0;font-size:11px}
90
+ .footer-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.footer-actions>.primary:last-child{margin-left:auto}.footer-actions:empty{display:none}.footer-meta{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px;margin-top:10px;font-size:10px}.footer-meta .text-button{font-size:10px}
91
+
92
+ @media(max-width:560px){.overlay{padding:8px;align-items:flex-end}
93
+ .panel{border-radius:var(--markee-radius) var(--markee-radius) var(--markee-control-radius) var(--markee-control-radius);max-height:94dvh}
94
+ .content{padding:20px 18px}
95
+ header{padding:20px 18px}
96
+ .nav{padding-left:10px;padding-right:10px}
97
+ .grid{grid-template-columns:1fr 1fr}
98
+ .metric{font-size:19px}
99
+ footer{padding:12px 18px}
100
+ .btn{padding:10px 12px}
101
+ .compact{padding:5px 9px}.text-button{padding:0}.footer-actions>.btn{flex:1;white-space:normal}.footer-actions>.primary:last-child{margin-left:0}
102
+ }
103
+
104
+ @media(prefers-reduced-motion:reduce){*{animation:none!important;scroll-behavior:auto!important}
105
+ }
106
+
107
+ `;var O={background:"#fbfcff",surface:"#ffffff",text:"#181b21",muted:"#687080",border:"#dce1ea",inputBackground:"#ffffff",inputText:"#202737",primary:"#282e3e",primaryText:"#ffffff",accent:"#c9f1a7",accentText:"#2b3c20",accentSurface:"#f1f7ea",accentSurfaceText:"#4e6535",accentBorder:"#cde3b5",selected:"#e9edf7",selectedText:"#242c47",info:"#edf1f9",infoText:"#52617b",error:"#fff0f0",errorText:"#9c3540",success:"#edf7e6",successText:"#40602f",link:"#4762a5",focusRing:"#5871c8",overlay:"#17203888"},F={background:"#151923",surface:"#202633",text:"#f0f3f8",muted:"#a6b1c3",border:"#434f63",inputBackground:"#10151e",inputText:"#f0f3f8",primary:"#dde6fa",primaryText:"#182238",accent:"#c9f1a7",accentText:"#24361a",accentSurface:"#243221",accentSurfaceText:"#c4e5ad",accentBorder:"#526b40",selected:"#313d53",selectedText:"#edf3ff",info:"#243149",infoText:"#bdcff1",error:"#44262e",errorText:"#ffbdc5",success:"#233b2b",successText:"#b5e4bf",link:"#adc5ff",focusRing:"#a1b4ff",overlay:"#030711b8"},N='Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',D=s=>`--markee-${s.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}`;function B(s,e){let t,a=(i={})=>{t?.(),t=void 0;let r=i.mode==="system"?window.matchMedia("(prefers-color-scheme: dark)"):void 0,n=()=>{let d=i.mode==="dark"||r?.matches?"dark":"light",c=d==="dark"?F:O;s.dataset.markeeTheme=d,s.style.setProperty("--markee-color-scheme",d);for(let u of Object.keys(c)){let w=i.colors?.[u];s.style.setProperty(D(u),w&&CSS.supports("color",w)?w:c[u])}s.style.setProperty("--markee-font-family",i.fontFamily&&CSS.supports("font-family",i.fontFamily)?i.fontFamily:N);let f=typeof i.radius=="number"&&Number.isFinite(i.radius)?Math.max(0,Math.min(48,i.radius)):22;s.style.setProperty("--markee-radius",`${f}px`),s.style.setProperty("--markee-card-radius",`${f*.64}px`),s.style.setProperty("--markee-control-radius",`${f*.45}px`),s.style.setProperty("--markee-small-radius",`${f*.32}px`)};n(),r&&(r.addEventListener("change",n),t=()=>r.removeEventListener("change",n))};return a(e),{set:a,destroy(){t?.(),t=void 0}}}var v=2628000n,$=10n**15n,S=(...s)=>s.reduce((e,t)=>e>t?e:t,0n);function A(s,e){let t=s.strategy==="rent",a=t?(BigInt(s.minimum)+$-1n)/$*$:e?1n:BigInt(s.minimum),i=s.entries.filter(u=>u.address.toLowerCase()!==e?.address.toLowerCase()),r=S(...i.map(u=>BigInt(u.score))),n=!!e&&e.address.toLowerCase()===s.top?.toLowerCase();if(!t){let u=BigInt(e?.totalFunds??"0");return{minimum:a,boost:S(a,r+$-u,n?u:0n),double:n&&u>0n}}let c=!!e&&s.wallet?.backedMarkee?.toLowerCase()===e.address.toLowerCase()?BigInt(s.wallet.flowRate):0n,f=S(BigInt(e?.aggregateRate??"0")-c);return{minimum:a,boost:S(a,(r-f)*v+$,n?c*v*2n:0n),double:n&&c>0n}}var M=s=>{let e=BigInt(s);return`${e/10n**18n}.${(e%10n**18n).toString().padStart(18,"0")}`.replace(/\.?0+$/,"")||"0"},o=(s,e,t="",a="")=>`<button type="button" class="btn ${t}" data-cmd="${s}" ${a}>${e}</button>`,z=(s,e)=>/^https:\/\//.test(s)?`<a href="${l(s)}" target="_blank" rel="noopener noreferrer">${l(e)} \u2197</a>`:"",L=class{constructor(e,t){this.client=e;this.options=t;if(typeof document>"u")throw new Error("Open the Markee modal in a browser.");this.previousFocus=document.activeElement,this.previousOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.host=document.createElement("div"),this.host.setAttribute("data-markee-modal",""),this.root=this.host.attachShadow({mode:"open"}),this.theme=B(this.host,t.theme??e.options.theme),document.body.appendChild(this.host),this.root.addEventListener("click",this.onClick),this.root.addEventListener("submit",this.onSubmit),this.root.addEventListener("keydown",this.onKey),this.root.addEventListener("input",()=>{this.captureDraft(),this.updateFields()}),this.render(),this.root.querySelector('[data-cmd="close"]')?.focus(),this.reload(),this.watchAccount()}host;root;theme;snapshot;account=null;tab="main";leaderboardOpen=!1;initialSelection=!1;renderedView="";form;review;authorization;adminResult;adminProcessing=!1;result;pending;wallets;busy=!1;error="";closed=!1;epoch=0;timer;accountTimer;watched;previousFocus;previousOverflow;abort=new AbortController;drafts=new Map;viewObserver;viewed=new Set;walletChanged=()=>{this.initialSelection=!1,this.review=void 0,this.authorization=void 0,this.form=void 0,this.result=void 0,this.error="Wallet changed. Refreshing this placement\u2026",this.reload()};close(){this.closed||(this.closed=!0,this.abort.abort(),clearTimeout(this.timer),clearTimeout(this.accountTimer),this.viewObserver?.disconnect(),this.theme.destroy(),this.watched?.removeListener?.("accountsChanged",this.walletChanged),this.watched?.removeListener?.("chainChanged",this.walletChanged),this.host.remove(),document.body.style.overflow=this.previousOverflow,this.previousFocus?.focus(),this.options.onClose?.())}setTheme(e){this.closed||this.theme.set(e)}async watchAccount(){try{let e=await this.client.provider();e!==this.watched&&(this.watched?.removeListener?.("accountsChanged",this.walletChanged),this.watched?.removeListener?.("chainChanged",this.walletChanged),this.watched=e||void 0,e?.on?.("accountsChanged",this.walletChanged),e?.on?.("chainChanged",this.walletChanged)),(await this.client.account())?.toLowerCase()!==this.account?.toLowerCase()&&!this.busy&&this.walletChanged()}catch{}this.closed||(this.accountTimer=setTimeout(()=>void this.watchAccount(),2e3))}async reload(e){let t=++this.epoch;try{let a=await this.client.account(),i=await this.client.snapshot(this.options.placement,a,e,this.abort.signal);if(this.closed||t!==this.epoch)return;if(this.account=a,this.snapshot=i,this.error="",this.form&&(this.form.entry=i.entries.find(n=>n.address.toLowerCase()===this.form.entry.address.toLowerCase())??this.form.entry),!this.initialSelection&&a){this.initialSelection=!0;let n=i.entries.find(d=>d.address.toLowerCase()===i.wallet?.backedMarkee?.toLowerCase())??i.entries.find(d=>d.owner.toLowerCase()===a.toLowerCase());n&&!this.form&&!this.drafts.get("create")?.message&&(this.form={kind:i.strategy==="rent"?"stream":"fund",entry:n})}this.options.placement.kind==="gardens-community"&&(this.adminResult=this.client.communityReceipt(this.options.placement)||void 0),this.pending=a?this.client.pending(this.options.placement,a):void 0;let r=this.client.pendingAuthorization();this.adminProcessing=!!r&&this.options.placement.kind==="gardens-community"&&r.account.toLowerCase()===a?.toLowerCase()&&r.community.toLowerCase()===this.options.placement.community.toLowerCase()&&r.chainId===this.options.placement.chainId,this.render(),this.pending&&this.poll()}catch(a){!this.closed&&t===this.epoch&&this.fail(a)}}fail(e){this.error=e instanceof Error?e.message:String(e),this.render()}async task(e){if(!this.busy){this.busy=!0,this.error="",this.render();try{await e()}catch(t){this.fail(t)}finally{this.busy=!1,this.render()}}}async connect(){if(!this.client.options.wallet){let e=await E();if(e.length>1){this.wallets=e,this.render();return}}this.host.style.visibility="hidden";try{await this.client.connect()}finally{this.host.style.visibility=""}await this.reload()}async prepare(e){if(this.pending)throw new Error("Check your pending transaction before preparing another action.");if(!this.account){await this.connect();return}this.review=await this.client.prepare(this.options.placement,e),this.authorization=void 0}async poll(){if(clearTimeout(this.timer),!(!this.pending||this.closed)){try{let e=this.pending,t=await this.client.verify(e);if(this.closed||this.pending?.hash!==e.hash)return;if(t.status!=="pending"){if(this.pending=void 0,this.result=t,this.error="",await this.reload(),t.createdMarkee&&this.snapshot?.strategy==="rent"){let a=this.snapshot.entries.find(r=>r.address.toLowerCase()===t.createdMarkee.toLowerCase()),i=e.preparation.action;if(i.kind==="create"){this.form={kind:"stream",entry:a||{address:t.createdMarkee,owner:e.preparation.transaction.from,message:i.message,name:i.name,score:"0",totalFunds:"0",aggregateRate:"0",legacyFloor:"0",moderated:!1,views:0}};let r=this.drafts.get("create")?.amount;r&&this.drafts.set(this.draftKey(),{amount:r})}}this.render();return}}catch(e){this.closed||this.fail(e)}!this.closed&&this.pending&&(this.timer=setTimeout(()=>void this.poll(),5e3))}}async applyAdmin(e){if(this.authorization=void 0,this.adminProcessing=e.status==="working"||e.status==="submitted",e.status==="complete"&&(this.adminResult=e.result,await this.reload()),e.status==="failed")throw new Error(e.error||"Community action failed.")}onClick=e=>{let t=e.target.closest("[data-cmd]");if(!t)return;let a=t.dataset.cmd;if(a==="close"){this.close();return}if(!this.busy){if(this.captureDraft(),a==="leaderboard"){this.leaderboardOpen=!this.leaderboardOpen,this.render();return}if(a==="create"){this.tab="main",this.form=void 0,this.review=void 0,this.render();return}if(a.startsWith("tab:")){this.tab=a.slice(4),this.review=void 0,this.authorization=void 0,this.render();return}if(a==="back"){this.review||this.authorization||this.wallets?(this.review=void 0,this.authorization=void 0,this.wallets=void 0):this.tab!=="main"?this.tab="main":this.form?.kind.startsWith("edit")?this.form={kind:this.snapshot.strategy==="rent"?"stream":"fund",entry:this.form.entry}:this.form=void 0,this.render();return}if(a.startsWith("preset:")&&this.snapshot){let i=this.root.querySelector("#amount");if(i){let r=A(this.snapshot,this.form?.entry);i.value=M((a==="preset:min"?r.minimum:r.boost).toString()),this.captureDraft(),this.updateFields()}return}if(a.startsWith("form:")){let[,i,r]=a.split(":"),n=this.snapshot?.entries.find(d=>d.address===r);n&&(this.form={kind:i,entry:n},this.tab="main"),this.render();return}this.task(async()=>{if(a==="connect")await this.connect();else if(a.startsWith("wallet:"))await this.client.connect(this.wallets[Number(a.slice(7))].provider),this.wallets=void 0,await this.reload();else if(a==="refresh")await this.reload();else if(a==="more")await this.reload(this.snapshot?.nextOffset??void 0);else if(a==="send"&&this.review){let i=this.review,r=await this.client.send(i);this.pending={preparation:i,hash:r},this.review=void 0,this.form=void 0,this.result=void 0,this.poll()}else if(a==="check")await this.poll();else if(["stop","withdraw-buffer","settle"].includes(a))await this.prepare({kind:a});else if(a.startsWith("claim-refund:")||a.startsWith("connect-refund:")||a.startsWith("claim-top:")){let[i,r]=a.split(":");await this.prepare({kind:i,markee:r})}else if(a==="activate"||a==="claim"){let i=this.options.placement;if(i.kind!=="gardens-community"||!this.account)return;let r=this.account;this.authorization={kind:a,account:r,value:await this.client.authorize(i,a)},this.review=void 0}else if(a==="sign"&&this.authorization){let i=this.authorization;try{await this.applyAdmin(await this.client.executeAuthorization(i.kind,i.value,i.account))}finally{this.client.pendingAuthorization()&&(this.authorization=void 0,this.adminProcessing=!0)}}else if(a==="admin-check")await this.applyAdmin(await this.client.resumeAuthorization());else if(a==="bridge"&&this.adminResult){let i=await this.client.bridgeStatus(this.adminResult.bridgeProtocol||"none",this.adminResult.transactionHash,this.snapshot.chainId,this.options.placement.chainId);this.error=`Treasury transfer status: ${i.status.replace(/_/g," ")}.`}})}};onSubmit=e=>{if(e.preventDefault(),this.busy)return;this.captureDraft();let t=e.target,a=new FormData(t),i=r=>String(a.get(r)||"");this.task(async()=>{if(t.dataset.form==="create")this.snapshot?.strategy==="rent"&&g(i("amount")),await this.prepare({kind:"create",message:i("message"),name:i("name"),...this.snapshot?.strategy==="sale"?{amount:g(i("amount"),!0)}:{}});else if(t.dataset.form==="balance")await this.prepare({kind:i("kind"),amount:g(i("amount"))});else if(this.form){let{kind:r,entry:n}=this.form;r==="fund"?await this.prepare({kind:r,markee:n.address,amount:g(i("amount"))}):r==="stream"?await this.prepare({kind:r,markee:n.address,monthlyAmount:g(i("amount"))}):r==="edit-message"?await this.prepare({kind:r,markee:n.address,message:i("message")}):await this.prepare({kind:r,markee:n.address,name:i("name")})}})};draftKey(){return this.tab!=="main"?this.tab:this.form?`${this.form.kind}:${this.form.entry.address}`:"create"}captureDraft(){let e=this.root.querySelector("form");e&&this.drafts.set(this.draftKey(),Object.fromEntries([...new FormData(e)].map(([t,a])=>[t,String(a)])))}onKey=e=>{let t=e;if(t.key==="Escape"&&(t.preventDefault(),this.close()),t.key!=="Tab"||this.host.style.visibility==="hidden")return;let a=[...this.root.querySelectorAll("button:not(:disabled),a[href],input,textarea,select")],i=a[0],r=a[a.length-1];t.shiftKey&&this.root.activeElement===i?(t.preventDefault(),r?.focus()):!t.shiftKey&&this.root.activeElement===r&&(t.preventDefault(),i?.focus())};field(e,t,a="",i="",r=!1){let n=e==="message"?this.snapshot?.maxMessageBytes:e==="name"?this.snapshot?.maxNameBytes:void 0,d=`${e==="name"?"":"required"} ${n===void 0?"":`data-byte-limit="${n}"`} aria-describedby="${e}-hint"`;return`<div class="field"><label for="${e}">${t}${n===void 0?"":`<span class="field-count" aria-hidden="true" data-count-for="${e}"></span>`}</label>${r?`<textarea id="${e}" name="${e}" ${d} placeholder="Your message on the sign">${l(a)}</textarea>`:`<input id="${e}" name="${e}" ${e==="amount"?'inputmode="decimal"':""} value="${l(a)}" ${d}>`}${i?`<small id="${e}-hint">${i}</small>`:""}</div>`}reviewHtml(){let e=this.review,t=e.action,a=t.kind==="create"||t.kind==="edit-message"?t.message:this.form?this.entryMessage(this.form.entry):void 0,i=e.review.lines.filter(r=>!r.startsWith("Message: "));return`<h2 class="review-title">${l(e.review.title)}</h2>${a?`<section class="message-card"><p class="message">${l(a)}</p></section>`:""}<section class="review"><ul>${i.map(r=>`<li>${l(r)}</li>`).join("")}</ul><div class="stat"><span>Estimated network fee</span><strong>${m(e.review.estimatedGasCost)} ETH</strong></div><div class="stat"><span>Wallet</span><strong class="mono">${C(e.transaction.from)}</strong></div></section><p class="note">${l(e.review.reward)}</p><p class="meta">This review expires in 5 minutes. Your wallet shows the final network fee.</p>`}authorizationHtml(){let e=this.authorization,t=e.value.typedData.message;return`<div class="eyebrow">Community authorization</div><h2>${e.kind==="activate"?"Activate this community\u2019s Markee":"Claim community revenue"}</h2><p>A signed authorization lets Markee\u2019s keeper execute this action. The server rechecks your current council role before submitting it.</p><div class="card stack">${["registryCommunity","recipient","leaderboardFactory"].filter(a=>t[a]).map(a=>`<div class="meta">${l(a)}<br><strong class="mono">${l(t[a])}</strong></div>`).join("")}${["claimAmount","gasCost","maxFeeAmount"].filter(a=>t[a]!==void 0).map(a=>`<div class="stat"><span>${a==="claimAmount"?"Revenue":a==="gasCost"?"Keeper network fee":"Maximum bridge fee"}</span><strong>${m(String(t[a]))} ETH</strong></div>`).join("")}</div><p class="note">${e.kind==="claim"?"Revenue is sent to the community council treasury. Bridged claims remain pending until destination delivery is confirmed.":"This creates the community board and its revenue vault on the Markee chain."}</p>`}entryMessage(e){return e.moderated===!1?e.message:e.moderated?"This message is hidden by moderation.":"Message unavailable while moderation loads."}entriesHtml(){let e=this.snapshot,t=[...e.entries].sort((r,n)=>BigInt(r.score)>BigInt(n.score)?-1:BigInt(r.score)<BigInt(n.score)?1:0),a=[...t.filter(r=>r.owner.toLowerCase()===this.account?.toLowerCase()),...t.filter(r=>r.owner.toLowerCase()!==this.account?.toLowerCase())],i=e.board&&[8453,11155111].includes(e.chainId)?`https://${e.chainId===8453?"www":"2staging"}.markee.xyz/markee/${e.board}`:"";return`<section class="leaderboard" aria-label="Markee leaderboard"><div class="row between">${o("leaderboard",`<span class="chevron" aria-hidden="true">${this.leaderboardOpen?"\u2303":"\u2304"}</span> Leaderboard`,"text-button",`aria-expanded="${this.leaderboardOpen}" aria-controls="markee-leaderboard"`)}${z(i,"Open in Markee")}</div>${this.leaderboardOpen?`<div class="leaderboard-list" id="markee-leaderboard">${a.map(r=>{let n=r.owner.toLowerCase()===this.account?.toLowerCase(),d=r.address.toLowerCase()===e.top?.toLowerCase(),c=r.address.toLowerCase()===this.form?.entry.address.toLowerCase();return`<article class="leaderboard-entry ${d?"winner":""} ${c?"selected-entry":""}"><div class="grow"><p class="message">${l(this.entryMessage(r))}</p><div class="row entry-caption">${d?'<span class="pill">Winner</span>':""}${n?'<span class="meta">Yours</span>':""}${r.moderated===!1&&r.name?`<span class="name">${l(r.name)}</span>`:""}</div></div><div class="entry-funding"><div class="meta mono">${e.strategy==="rent"?`${m((BigInt(r.score)*v).toString())} ETH/mo`:`${m(r.totalFunds)} ETH`}</div><span class="meta">${r.views===null?"Views unavailable":`${r.views.toLocaleString()} views`}</span>${o(`form:${e.strategy==="sale"?"fund":"stream"}:${r.address}`,c?"Selected":"Fund",`compact ${c?"primary":""}`,`aria-pressed="${c}" aria-label="${c?"Selected":"Fund"} message ${C(r.address)}"`)}</div></article>`}).join("")||'<p class="empty">No Markees yet. Create the first one.</p>'}${e.strategy==="rent"&&e.rankedTop&&e.rankedTop.toLowerCase()!==e.top?.toLowerCase()?`<p class="note">The ranking has changed. ${o(`claim-top:${e.rankedTop}`,"Refresh winner","compact")}</p>`:""}${e.nextOffset!==null?o("more","Browse more messages","full"):""}</div>`:""}</section>`}selectedMessageHtml(e){let t=this.snapshot,a=t.wallet,i=e.owner.toLowerCase()===this.account?.toLowerCase(),r=t.entries.find(c=>c.owner.toLowerCase()===this.account?.toLowerCase()),n=e.address.toLowerCase()===t.top?.toLowerCase(),d=e.address.toLowerCase()===a?.backedMarkee?.toLowerCase()&&BigInt(a?.flowRate??"0")>0n;return`<section class="message-card ${n?"winner":""}"><div class="row between"><span class="eyebrow">${i?"Your Markee":"Markee you\u2019re funding"}</span>${n?'<span class="pill">Winner</span>':""}</div><p class="message">${l(this.entryMessage(e))}</p>${e.moderated===!1&&e.name?`<p class="name">${l(e.name)}</p>`:""}<div class="row message-tools">${i?o(`form:edit-message:${e.address}`,"Edit message","text-button")+o(`form:edit-name:${e.address}`,"Edit name","text-button"):""}${!i&&r?o(`form:${t.strategy==="rent"?"stream":"fund"}:${r.address}`,"Back to your Markee","text-button"):""}${o("create","Create your own Markee","text-button")}${t.strategy==="rent"&&this.account?o(`claim-refund:${e.address}`,"Collect refund","text-button"):""}</div><div class="message-stats">${t.strategy==="rent"?`<span class="meta">${d?n?"\u25CF Active stream":a?.poolConnected?"\u25CF Stream refunded while not the winner":"\u25CF Connect your refund pool in Deposit manager":"No active stream from your wallet to this message"}</span><div class="stats-grid"><div><span class="stat-label">Ranking rate</span><strong>${m((BigInt(e.score)*v).toString())} ETH/mo</strong></div><div><span class="stat-label">Your rate</span><strong>${m((d?BigInt(a.flowRate)*v:0n).toString())} ETH/mo</strong></div>${a?`<div><span class="stat-label">ETHx balance</span><strong>${m(a.ethx)} ETHx</strong></div><div><span class="stat-label">Funds remaining</span><strong title="Across all of your ETHx flows">${a.runwaySeconds===null?"No net outflow":`${(Number(a.runwaySeconds)/86400).toFixed(1)} days`}</strong></div>`:""}</div>${BigInt(e.legacyFloor)>0n?`<p class="meta">Ranking includes a decaying legacy floor of ${m((BigInt(e.legacyFloor)*v).toString())} ETH/mo.</p>`:""}`:`<div class="stats-grid"><div><span class="stat-label">Total funded</span><strong>${m(e.totalFunds)} ETH</strong></div><div><span class="stat-label">Views</span><strong>${e.views===null?"Unavailable":e.views.toLocaleString()}</strong></div></div>`}</div></section>`}fundingHtml(e){let t=this.snapshot,a=t.wallet,i=t.strategy==="rent",r=A(t,e),n=!!e&&e.address.toLowerCase()===a?.backedMarkee?.toLowerCase(),d=i&&n?BigInt(a.flowRate)*v:0n,c=BigInt(t.minimum)>r.minimum?BigInt(t.minimum):r.minimum,f=d>c?d:c,u=!i&&e?1n:BigInt(t.minimum);return`<section class="funding-card"><label for="amount">${i?"Monthly streaming rate":"Payment amount"}</label><div class="amount-row"><div class="amount-value"><input id="amount" name="amount" inputmode="decimal" value="${l(M(f.toString()))}" required aria-describedby="amount-hint"><span class="mono">${i?"ETHx/mo":"ETH"}</span></div><div class="amount-presets">${o("preset:min","MIN","compact mono",'title="Use the minimum amount"')}${o("preset:boost",r.double?"2\xD7":"WIN","compact mono",'title="Suggest an amount using the loaded ranking"')}</div></div><div class="funding-caption"><span id="amount-hint">Minimum ${M(u.toString())} ${i?"ETH/mo":"ETH"}</span><span>${a?`Balance ${m(a.eth)} ETH`:"Connect to see your balance"}</span></div><div class="funding-tools">${o("tab:wallet",i?"Deposit manager \u2192":"Wallet & rewards \u2192","text-button")}<span class="meta">${i?a?.runwaySeconds!=null?`${(Number(a.runwaySeconds)/86400).toFixed(1)} days remaining`:a?`${m(a.ethx)} ETHx available`:"ETH funds your ETHx balance":"One-time payment"}</span></div></section><p class="meta funding-hint">${i?"Unspent rent is refunded while your message is not the winner. You can stop streaming at any time.":e?"Adding funds keeps the current message owner.":"Your connected wallet will own this message."} Shortcuts use the loaded ranking; positions can change.</p>`}formHtml(){let e=this.snapshot,t=this.form;return t?.kind.startsWith("edit")?`<form id="markee-action" data-form="entry"><section class="message-card"><div class="eyebrow">Your Markee</div>${t.kind==="edit-message"?this.field("message","Markee message",t.entry.message,`${e.maxMessageBytes} UTF-8 bytes maximum.`,!0):this.field("name","Your name (optional)",t.entry.name,`${e.maxNameBytes} UTF-8 bytes maximum.`)}</section></form>`:`<form id="markee-action" data-form="${t?"entry":"create"}">${t?this.selectedMessageHtml(t.entry):`<section class="message-card editor-card">${this.field("message","Markee message","",`${e.maxMessageBytes} UTF-8 bytes maximum.`,!0)}${this.field("name","Your name (optional)","",`${e.maxNameBytes} UTF-8 bytes maximum.`)}</section>`}${this.fundingHtml(t?.entry)}${!t&&e.strategy==="rent"?'<p class="note">First create your message, then review and confirm its stream. Your chosen rate is kept for the next step.</p>':""}</form>`}updateFields(){for(let t of this.root.querySelectorAll("[data-byte-limit]")){let a=Number(t.dataset.byteLimit),i=new TextEncoder().encode(t.value).length;t.setCustomValidity(i>a?`Use at most ${a} UTF-8 bytes.`:"");let r=this.root.querySelector(`[data-count-for="${t.name}"]`);r&&(r.textContent=`${i}/${a}`)}let e=this.root.querySelector("#markee-action #amount");if(e&&this.snapshot){let t=A(this.snapshot,this.form?.entry),a;try{a=BigInt(g(e.value,this.snapshot.strategy==="sale"))}catch{}let i=this.snapshot.strategy==="sale"&&this.form?1n:BigInt(this.snapshot.minimum);e.setCustomValidity(a===void 0?"Enter a valid ETH amount.":a<i?`Use at least ${M(i.toString())} ETH${this.snapshot.strategy==="rent"?" per month":""}.`:""),this.root.querySelector('[data-cmd="preset:min"]')?.setAttribute("aria-pressed",String(a===t.minimum)),this.root.querySelector('[data-cmd="preset:boost"]')?.setAttribute("aria-pressed",String(a===t.boost))}}walletHtml(){let e=this.snapshot,t=e.wallet;if(!t)return'<p class="empty">Connect your wallet to see balances and manage your Markee.</p>'+o("connect","Connect app wallet","primary full");let a=`<h2>Your wallet</h2><p class="meta mono">${l(t.address)}</p><div class="grid"><div class="card"><span class="meta">Native ETH</span><div class="metric">${m(t.eth)}</div></div><div class="card"><span class="meta">MARKEE</span><div class="metric">${t.markee===null?"Unavailable":m(t.markee,2)}</div></div></div>`;return e.strategy==="sale"?a+'<p class="note">Payments reward the paying wallet when this board has RevNet enabled. Manage your messages from the leaderboard.</p>':(a+=`<div class="divider"></div><div class="card"><div class="stat"><span>Available ETHx</span><strong>${m(t.ethx)} ETHx</strong></div><div class="stat"><span>This board\u2019s monthly rate</span><strong>${m((BigInt(t.flowRate)*2628000n).toString())} ETH</strong></div><div class="stat"><span>Refund flow / month</span><strong>${m((BigInt(t.refundRate)*2628000n).toString())} ETHx</strong></div><div class="stat"><span>Runway across all ETHx flows</span><strong>${t.runwaySeconds===null?"No net outflow":`${(Number(t.runwaySeconds)/86400).toFixed(1)} days`}</strong></div><p class="meta">Snapshot at ${new Date(t.observedAt*1e3).toLocaleTimeString()}. Balances and rates can change.</p><div class="row">${BigInt(t.flowRate)>0n?o("stop","Stop stream","danger"):""}${t.backedMarkee&&!t.poolConnected?o(`connect-refund:${t.backedMarkee}`,"Connect refund pool","accent"):""}${o("refresh","Refresh balances")}</div></div><div class="divider"></div><h3>Manage ETHx</h3><form data-form="balance"><div class="grid"><div><label for="kind">Action</label><select id="kind" name="kind"><option value="wrap">Deposit ETH \u2192 ETHx</option><option value="unwrap">Withdraw ETHx \u2192 ETH</option></select></div>${this.field("amount","Amount","0.001")}</div><button type="submit" class="btn">Review balance change</button></form><div class="divider"></div><div class="card"><h3>Buffer and rewards</h3><div class="stat"><span>Refundable board buffer</span><strong>${m(t.withdrawableDeposit)} ETHx</strong></div><div class="stat"><span>Accrued rent to settle</span><strong>${m(t.pendingSettlement)} ETH</strong></div><p class="meta">Rewards come from actual rent spent, not the ETH deposited into your wallet.</p><div class="row">${BigInt(t.withdrawableDeposit)>0n?o("withdraw-buffer","Recover buffer"):""}${BigInt(t.pendingSettlement)>0n?o("settle",e.revnetEnabled?"Claim MARKEE":"Settle rent","primary"):""}${t.backedMarkee&&BigInt(t.poolClaimable)>0n?o(`claim-refund:${t.backedMarkee}`,"Collect pool refund"):""}</div></div>`,a)}communityHtml(){let e=this.snapshot.community;return e?`<h2>Community treasury</h2><p class="muted">Community council members can activate Markee and collect community revenue.</p><div class="card"><div class="stat"><span>Integration</span><strong>${e.integration.status==="active"?"Active":"Not activated"}</strong></div><div class="stat"><span>Available revenue</span><strong>${m(e.revenue.claimableAmount)} ETH</strong></div>${e.permissions?.councilSafe?`<p class="meta">Council treasury<br><span class="mono">${l(e.permissions.councilSafe)}</span></p>`:""}<div class="row">${this.adminProcessing?'<p class="note">A signed community action is processing. Check it before authorizing another.</p>'+o("admin-check","Check community action","primary"):this.account?e.integration.status!=="active"&&e.permissions?.canActivate?o("activate","Activate community Markee","primary"):e.permissions?.canClaim&&BigInt(e.revenue.claimableAmount)>0n?o("claim","Review revenue claim","primary"):'<span class="meta">No authorized action is currently available for this wallet.</span>':o("connect","Connect council wallet","primary")}</div></div>${this.adminResult?`<div class="success">Source transaction confirmed. ${z(this.adminResult.transactionUrl||"","View transaction")}${this.adminResult.bridged?"<p>Bridge delivery is tracked separately from the source transaction.</p>":""}${o("bridge","Check transfer status")}</div>`:""}`:""}title(){let e=this.snapshot;return this.wallets?"Connect your wallet":this.review?"Review payment":this.authorization?"Review authorization":this.pending?"Payment in progress":this.tab==="wallet"?e?.strategy==="rent"?"Deposit manager":"Wallet & rewards":this.tab==="community"||e&&!e.board?"Community treasury":this.form?.kind.startsWith("edit")?"Edit your Markee":this.form?this.form.entry.owner.toLowerCase()!==this.account?.toLowerCase()?"Fund a Markee":e?.strategy==="sale"?"Fund your Markee":BigInt(e?.wallet?.flowRate??"0")>0n?"Manage your Markee stream":"Stream to your Markee":"Change the Markee Sign"}footerHtml(){let e=this.snapshot,t="";if(this.wallets)t=o("back","Back");else if(this.review)t=o("back","Back")+o("send","Confirm in wallet","primary");else if(this.authorization)t=o("back","Back")+o("sign","Sign authorization","primary");else if(this.pending)t=o("check","Check transaction","primary");else if(e&&this.tab==="main"&&e.board){let a=this.form?.kind.startsWith("edit");t=(a?o("back","Cancel"):e.strategy==="rent"&&BigInt(e.wallet?.flowRate??"0")>0n?o("stop","Stop streaming","danger"):"")+(this.account?`<button class="btn primary" type="submit" form="markee-action">${a?"Review change":!this.form&&e.strategy==="rent"?"Review creation":"Review payment info"}</button>`:o("connect","Connect wallet","primary"))}else e&&(t=o("tab:main","Back to Markee"));return`<footer><div class="footer-actions">${t}</div><div class="footer-meta"><span>Powered by Markee${this.account?` \xB7 ${C(this.account)}`:""}</span>${e?.community&&e.board&&this.tab!=="community"&&!this.review&&!this.authorization&&!this.pending?o("tab:community","Community treasury","text-button"):""}</div></footer>`}render(){if(this.closed)return;let e=this.root.activeElement,t=e?.dataset.cmd,a=e?.id,i=this.wallets?"wallets":this.review?`review:${this.review.id}`:this.authorization?`authorization:${this.authorization.value.nonce}`:this.pending?`pending:${this.pending.hash}`:`${this.tab}:${this.form?.kind??"create"}:${this.form?.entry.address??""}`,r=i===this.renderedView?this.root.querySelector(".content")?.scrollTop??0:0;this.renderedView=i;let n=!document.activeElement||document.activeElement===document.body||document.activeElement===this.host,d=this.snapshot,c="";this.wallets?c=`<h2>Choose your wallet</h2><div class="wallets">${this.wallets.map((h,b)=>o(`wallet:${b}`,l(h.name))).join("")}</div>`:this.review?c=this.reviewHtml():this.authorization?c=this.authorizationHtml():d?this.pending?c=`<h2>Waiting for confirmation</h2><p>Your transaction was sent. You can close this modal and come back to check it.</p><p class="meta mono">${l(this.pending.hash)}</p><p class="note">Do not send a second payment while this transaction is pending.</p>`:!d.board||this.tab==="community"?c=this.communityHtml():this.tab==="wallet"?c=this.walletHtml():c=`${this.form?.kind.startsWith("edit")?"":this.entriesHtml()}${this.formHtml()}`:c=this.error?o("refresh","Retry","primary"):'<div class="loading"><span class="spinner"></span>Loading Markee\u2026</div>';let f=this.result?`<div class="${this.result.status==="reverted"||this.result.settlement==="deferred"?"error":"success"}">${this.result.status==="reverted"?"Transaction reverted. No action was completed.":this.result.settlement==="deferred"?"The transaction confirmed, but reward settlement was deferred. Your claim remains available.":`Transaction confirmed.${this.result.tokensReceived&&BigInt(this.result.tokensReceived)>0n?` ${m(this.result.tokensReceived,2)} MARKEE delivered to your wallet.`:""}`} ${z(this.result.explorerUrl,"View receipt")}</div>`:"";this.root.innerHTML=`<style>${R}</style><div class="overlay"><section class="panel" role="dialog" aria-modal="true" aria-labelledby="markee-title"><header><div class="grow"><h1 id="markee-title">${l(this.title())}</h1><p class="header-caption">${l(d?.name||"Markee")}${d?` \xB7 ${d.strategy==="rent"?"For rent":"For sale"}${d.chainId===8453?" \xB7 Base":d.chainId===11155111?" \xB7 Sepolia testnet":""}`:""}</p></div><button class="close" data-cmd="close" aria-label="Close Markee">\xD7</button></header><main class="content" aria-busy="${this.busy}"><div role="status" aria-live="polite">${this.busy?'<p class="meta"><span class="spinner"></span>Working\u2026</p>':""}${this.error?`<div class="error">${l(this.error)}</div>`:""}${f}</div>${d?.warnings.map(h=>`<div class="note">${l(h)}</div>`).join("")||""}${c}</main>${this.footerHtml()}</section></div>`,this.busy&&this.root.querySelectorAll('button:not([data-cmd="close"])').forEach(h=>{h.disabled=!0});let u=this.drafts.get(this.draftKey());if(u)for(let h of this.root.querySelectorAll("input,textarea,select"))u[h.name]!==void 0&&(h.value=u[h.name]);this.updateFields();let w=this.root.querySelector(".content");w&&(w.scrollTop=r),this.viewObserver?.disconnect();let I=this.root.querySelector(".winner"),y=d?.entries.find(h=>h.address.toLowerCase()===d.top?.toLowerCase()),P=y?`${y.address}:${y.message}`:"";I&&y?.moderated===!1&&!this.viewed.has(P)&&typeof IntersectionObserver<"u"&&(this.viewObserver=new IntersectionObserver(h=>{h.some(b=>b.isIntersecting)&&(this.viewObserver?.disconnect(),this.viewed.add(P),this.client.recordView(y.address,y.message,window.location.origin).catch(()=>{this.viewed.delete(P)}))},{threshold:.5}),this.viewObserver.observe(I)),n&&((a?this.root.getElementById(a):t?[...this.root.querySelectorAll("[data-cmd]")].find(b=>b.dataset.cmd===t):null)??this.root.querySelector('[data-cmd="close"]'))?.focus({preventScroll:!0})}};function q(s={}){let e=new k(s),t,a=s.theme;return{client:e,open(i){return t?.close(),t=new L(e,{...i,theme:i.theme??a}),t},setTheme(i){a=i??{},t?.setTheme(a)},close(){t?.close(),t=void 0}}}export{H as DEFAULT_MARKEE_API_URL,k as MarkeeClient,p as MarkeeError,L as MarkeeModal,q as createMarkee,E as discoverWallets,m as formatEth,g as parseEth};
@@ -0,0 +1,80 @@
1
+ import { MarkeeClient } from './client.js';
2
+ import type { MarkeeTheme } from './types.js';
3
+ import type { Placement, SDKOptions } from './types.js';
4
+ export interface ModalOptions {
5
+ placement: Placement;
6
+ onClose?: () => void;
7
+ theme?: MarkeeTheme;
8
+ }
9
+ export declare class MarkeeModal {
10
+ readonly client: MarkeeClient;
11
+ readonly options: ModalOptions;
12
+ private host;
13
+ private root;
14
+ private theme;
15
+ private snapshot?;
16
+ private account;
17
+ private tab;
18
+ private leaderboardOpen;
19
+ private initialSelection;
20
+ private renderedView;
21
+ private form?;
22
+ private review?;
23
+ private authorization?;
24
+ private adminResult?;
25
+ private adminProcessing;
26
+ private result?;
27
+ private pending?;
28
+ private wallets?;
29
+ private busy;
30
+ private error;
31
+ private closed;
32
+ private epoch;
33
+ private timer?;
34
+ private accountTimer?;
35
+ private watched?;
36
+ private previousFocus;
37
+ private previousOverflow;
38
+ private abort;
39
+ private drafts;
40
+ private viewObserver?;
41
+ private viewed;
42
+ private walletChanged;
43
+ constructor(client: MarkeeClient, options: ModalOptions);
44
+ close(): void;
45
+ /** Replace the theme without resetting the active form, wallet, or transaction. */
46
+ setTheme(theme?: MarkeeTheme): void;
47
+ private watchAccount;
48
+ private reload;
49
+ private fail;
50
+ private task;
51
+ private connect;
52
+ private prepare;
53
+ private poll;
54
+ private applyAdmin;
55
+ private onClick;
56
+ private onSubmit;
57
+ private draftKey;
58
+ private captureDraft;
59
+ private onKey;
60
+ private field;
61
+ private reviewHtml;
62
+ private authorizationHtml;
63
+ private entryMessage;
64
+ private entriesHtml;
65
+ private selectedMessageHtml;
66
+ private fundingHtml;
67
+ private formHtml;
68
+ private updateFields;
69
+ private walletHtml;
70
+ private communityHtml;
71
+ private title;
72
+ private footerHtml;
73
+ private render;
74
+ }
75
+ export declare function createMarkee(options?: SDKOptions): {
76
+ client: MarkeeClient;
77
+ open(options: ModalOptions): MarkeeModal;
78
+ setTheme(next?: MarkeeTheme): void;
79
+ close(): void;
80
+ };
@@ -0,0 +1,8 @@
1
+ import type { Entry, Snapshot } from './types.js';
2
+ export declare const MONTH = 2628000n;
3
+ /** Input suggestions only. The API still validates and simulates the final action. */
4
+ export declare function fundingPresets(snapshot: Snapshot, entry?: Entry): {
5
+ minimum: bigint;
6
+ boost: bigint;
7
+ double: boolean;
8
+ };
@@ -0,0 +1,20 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { Placement, SDKOptions } from './types.js';
3
+ export interface MarkeeModalProps extends SDKOptions {
4
+ open: boolean;
5
+ placement: Placement;
6
+ onClose(): void;
7
+ }
8
+ /** No wagmi, ConnectKit, Privy, or React provider is required by this wrapper. */
9
+ export declare function MarkeeModal(props: MarkeeModalProps): null;
10
+ export declare function MarkeeButton({ children, onClick, ...props }: {
11
+ children?: ReactNode;
12
+ onClick(): void;
13
+ className?: string;
14
+ disabled?: boolean;
15
+ }): import("react").DetailedReactHTMLElement<{
16
+ type: string;
17
+ onClick: () => void;
18
+ className?: string;
19
+ disabled?: boolean;
20
+ }, HTMLElement>;
package/dist/react.js ADDED
@@ -0,0 +1,107 @@
1
+ "use client";import{createElement as K,useEffect as O,useRef as N}from"react";var F="https://markee-git-feature-markee-sdk-1hive.vercel.app/api/sdk/v1",p=class extends Error{constructor(t,a="MARKEE_ERROR",i){super(t);this.code=a;this.status=i;this.name="MarkeeError"}};async function P(){if(typeof window>"u")return[];let n=new Map,e=a=>{let i=a.detail;i?.provider?.request&&typeof i.info?.uuid=="string"&&n.set(i.info.uuid,{uuid:i.info.uuid,name:String(i.info.name).slice(0,80),provider:i.provider})};window.addEventListener("eip6963:announceProvider",e),window.dispatchEvent(new Event("eip6963:requestProvider")),await new Promise(a=>setTimeout(a,150)),window.removeEventListener("eip6963:announceProvider",e);let t=window.ethereum;return!n.size&&t?.request&&n.set("injected",{uuid:"injected",name:"Browser wallet",provider:t}),[...n.values()]}var x=n=>typeof n=="string"&&/^0x[\da-f]{40}$/i.test(n),$=n=>typeof n=="string"&&/^0x[\da-f]{64}$/i.test(n),T=class{constructor(e={}){this.options=e;let t=new URL(e.apiUrl??F);if(t.protocol!=="https:"&&!(t.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)))throw new p("Use an HTTPS Markee API URL (HTTP is allowed on localhost).");this.apiUrl=t.href.replace(/\/$/,""),this.fetcher=e.fetch||globalThis.fetch.bind(globalThis)}apiUrl;fetcher;injected=null;sending=!1;inMemoryPending=[];adminMemory=null;communityReceipts={};async request(e,t,a){let i=await this.fetcher(`${this.apiUrl}/${e}`,{method:t===void 0?"GET":"POST",body:t===void 0?void 0:JSON.stringify(t),headers:t===void 0?void 0:{"Content-Type":"application/json"},credentials:"omit",signal:a||AbortSignal.timeout(55e3)}),r=await i.json().catch(()=>null);if(!i.ok)throw new p(r?.error||`Markee returned HTTP ${i.status}.`,r?.code,i.status);return r}async provider(){return this.options.wallet?this.options.wallet.getProvider():this.injected}async account(){let e=await this.provider();if(!e)return null;let t=await e.request({method:"eth_accounts"});return Array.isArray(t)&&x(t[0])?t[0]:null}async connect(e){let t=await this.provider();if(this.options.wallet)t||(t=await this.options.wallet.connect?.()||null);else{if(t=e||t,!t){let i=await P();if(i.length>1)throw new p("Select a browser wallet.","SELECT_WALLET");t=i[0]?.provider||null}this.injected=t}if(!t)throw new p("Connect a wallet in the app or install a browser wallet to continue.","NO_WALLET");let a=await t.request({method:"eth_requestAccounts"});if(!Array.isArray(a)||!x(a[0]))throw new p("The wallet did not return an account.");return a[0]}async snapshot(e,t,a,i){let r=new URLSearchParams({placement:JSON.stringify(e)});return t&&r.set("account",t),a!==void 0&&r.set("offset",String(a)),this.request(`snapshot?${r}`,void 0,i)}async prepare(e,t){let a=await this.account();if(!a)throw new p("Connect your wallet first.","NOT_CONNECTED");return this.request("prepare",{placement:e,account:a,action:t})}async ensureWallet(e,t){let a=await this.provider();if(!a||(await this.account())?.toLowerCase()!==e.toLowerCase())throw new p("Your wallet account changed. Review this action again.","ACCOUNT_CHANGED");let i=Number(await a.request({method:"eth_chainId"}));if(i!==t&&await a.request({method:"wallet_switchEthereumChain",params:[{chainId:`0x${t.toString(16)}`}]}),i=Number(await a.request({method:"eth_chainId"})),i!==t||(await this.account())?.toLowerCase()!==e.toLowerCase())throw new p("The wallet account or network changed. Review again.","WALLET_CHANGED");return a}async send(e){if(this.sending)throw new p("A wallet request is already in progress.");if(e.expiresAt<=Date.now())throw new p("This review expired. Prepare the action again.","REVIEW_EXPIRED");if(this.pending(e.placement,e.transaction.from))throw new p("A transaction is still pending for this wallet and placement. Check it before submitting again.","PENDING_TRANSACTION");this.sending=!0;try{let a=await this.ensureWallet(e.transaction.from,e.transaction.chainId);if(e.expiresAt<=Date.now())throw new p("This review expired. Prepare the action again.","REVIEW_EXPIRED");let{chainId:i,...r}=e.transaction,s=await a.request({method:"eth_sendTransaction",params:[r]});if(!$(s))throw new p("The wallet returned an unexpected transaction identifier. Check your wallet before retrying.");return this.persist([...this.records(),{preparation:e,hash:s}]),this.options.onEvent?.({type:"submitted",hash:s}),s}catch(a){throw this.options.onEvent?.({type:"error",message:a instanceof Error?a.message:String(a)}),a}finally{this.sending=!1}}async verify(e){let t=await this.request("verify",{id:e.preparation.id,hash:e.hash});return t.status!=="pending"&&(this.persist(this.records().filter(a=>a.hash!==e.hash)),t.status==="confirmed"&&this.options.onEvent?.({type:"confirmed",hash:t.hash})),t}async reconcile(e,t){if(!$(t))throw new p("Enter a valid replacement transaction hash.");let a=await this.request("verify",{id:e.preparation.id,hash:t});return a.status!=="pending"&&this.persist(this.records().filter(i=>i.hash!==e.hash)),a}storageKey(){return`markee:sdk:v1:pending:${this.apiUrl}`}records(){try{if(typeof localStorage>"u")return this.inMemoryPending;let e=JSON.parse(localStorage.getItem(this.storageKey())||"[]");return Array.isArray(e)?e.filter(t=>$(t?.hash)&&typeof t.preparation?.id=="string"&&x(t.preparation?.transaction?.from)&&t.preparation?.placement):[]}catch{return this.inMemoryPending}}persist(e){this.inMemoryPending=e;try{typeof localStorage<"u"&&localStorage.setItem(this.storageKey(),JSON.stringify(e))}catch{}}pending(e,t){return this.records().find(a=>JSON.stringify(a.preparation.placement).toLowerCase()===JSON.stringify(e).toLowerCase()&&a.preparation.transaction.from.toLowerCase()===t.toLowerCase())}async quote(e){return this.request(`community/quote?${new URLSearchParams({chainId:String(e.chainId),community:e.community})}`)}async authorize(e,t){let a=await this.account();if(!a)throw new p("Connect your community council wallet first.");return this.request(`community/${t}`,{action:"challenge",chainId:e.chainId,community:e.community,account:a})}async executeAuthorization(e,t,a){if(this.pendingAuthorization())throw new p("An earlier community authorization is still processing. Check its status first.");let i=t.typedData,s=await(await this.ensureWallet(a,i.domain.chainId)).request({method:"eth_signTypedData_v4",params:[a,JSON.stringify({...i,types:{...i.types,EIP712Domain:[{name:"name",type:"string"},{name:"version",type:"string"},{name:"chainId",type:"uint256"},{name:"verifyingContract",type:"address"}]}})]});if(typeof s!="string"||!/^0x[\da-f]+$/i.test(s))throw new p("Wallet did not return a typed-data signature.");return this.saveAuthorization({kind:e,nonce:t.nonce,signature:s,account:a,community:i.domain.verifyingContract,chainId:i.domain.chainId}),this.resumeAuthorization()}pendingAuthorization(){try{let e=typeof localStorage>"u"?this.adminMemory:JSON.parse(localStorage.getItem(`${this.storageKey()}:admin`)||"null");return e&&["activate","claim"].includes(e.kind)&&x(e.account)&&x(e.community)&&typeof e.nonce=="string"&&typeof e.signature=="string"?e:null}catch{return this.adminMemory}}saveAuthorization(e){this.adminMemory=e;try{typeof localStorage<"u"&&localStorage.setItem(`${this.storageKey()}:admin`,JSON.stringify(e))}catch{}}async resumeAuthorization(){let e=this.pendingAuthorization();if(!e)throw new p("There is no pending community authorization.");let t=await this.request(`community/${e.kind}`,{action:"verify",nonce:e.nonce,signature:e.signature});if(t.status==="complete"&&t.result){let a=`${e.chainId}:${e.community.toLowerCase()}`;this.communityReceipts[a]=t.result;try{typeof localStorage<"u"&&localStorage.setItem(`${this.storageKey()}:community:${a}`,JSON.stringify(t.result))}catch{}}return(t.status==="complete"||t.status==="failed")&&this.saveAuthorization(null),t}communityReceipt(e){let t=`${e.chainId}:${e.community.toLowerCase()}`;try{let a=typeof localStorage>"u"?this.communityReceipts[t]:JSON.parse(localStorage.getItem(`${this.storageKey()}:community:${t}`)||"null");return a&&$(a.transactionHash)?a:null}catch{return this.communityReceipts[t]||null}}bridgeStatus(e,t,a,i){return this.request(`community/status?${new URLSearchParams({protocol:e,transactionHash:t,fromChainId:String(a),toChainId:String(i)})}`)}recordView(e,t,a){return this.request("views",{address:e,message:t,url:a})}};function v(n,e=!1){let t=n.trim();if(!/^\d+(\.\d{0,18})?$/.test(t))throw new Error("Enter an ETH amount with up to 18 decimal places.");let[a,i=""]=t.split("."),r=BigInt(a)*10n**18n+BigInt(i.padEnd(18,"0"));if(!e&&r===0n||r>=2n**256n)throw new Error("Enter a positive amount.");return r.toString()}function m(n,e=6){let t=BigInt(n),a=t<0n?"-":"",i=t<0n?-t:t,r=(i%10n**18n).toString().padStart(18,"0").slice(0,e).replace(/0+$/,"");return i>0n&&i<10n**BigInt(18-e)?`${a}<0.${"0".repeat(e-1)}1`:`${a}${i/10n**18n}${r?`.${r}`:""}`}function E(n){return`${n.slice(0,6)}\u2026${n.slice(-4)}`}function l(n){return String(n).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e])}var H=`
2
+ :host{all:initial;font-family:var(--markee-font-family);color:var(--markee-text);font-size:14px;line-height:1.5;color-scheme:var(--markee-color-scheme)}
3
+
4
+ *{box-sizing:border-box}
5
+ button,input,textarea,select{font:inherit}
6
+ button{cursor:pointer}
7
+ button:disabled{opacity:.5;cursor:wait}
8
+ button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,a:focus-visible{outline:3px solid var(--markee-focus-ring);outline-offset:3px}
9
+
10
+ .overlay{position:fixed;inset:0;z-index:10000;background:var(--markee-overlay);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;padding:24px}
11
+
12
+ .panel{background:var(--markee-background);border:1px solid var(--markee-border);border-radius:var(--markee-radius);width:min(512px,100%);max-height:92dvh;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 24px 100px #10183244;position:relative}
13
+
14
+ header{flex-shrink:0;padding:18px 24px;display:flex;gap:16px;align-items:flex-start;border-bottom:1px solid var(--markee-border)}
15
+ .brand{width:40px;height:40px;display:grid;place-items:center;border-radius:var(--markee-control-radius);background:var(--markee-primary);color:var(--markee-primary-text);font-weight:900;font-size:24px}
16
+ .grow{flex:1;min-width:0}
17
+ h1{font-size:18px;line-height:1.25;margin:0 0 4px}
18
+ h2{font-size:18px;line-height:1.3;margin:0 0 12px}
19
+ h3{font-size:16px;margin:0 0 8px}
20
+ p{margin:8px 0 14px}
21
+ .muted,small{color:var(--markee-muted)}
22
+ .eyebrow{font-size:11px;text-transform:uppercase;letter-spacing:1.3px;font-weight:700;color:var(--markee-accent-surface-text)}
23
+ .pill{display:inline-flex;border-radius:var(--markee-small-radius);background:var(--markee-accent-surface);color:var(--markee-accent-surface-text);padding:3px 7px;font-size:11px;font-weight:700}
24
+ .close{border:0;background:none;color:var(--markee-muted);font-size:23px;padding:5px 10px;border-radius:var(--markee-small-radius)}
25
+ .nav{display:flex;gap:4px;padding:12px 24px 0;overflow:auto}
26
+ .nav button{white-space:nowrap;border:0;background:transparent;padding:10px 12px;color:var(--markee-muted);border-radius:var(--markee-control-radius);font-size:13px}
27
+ .nav button[aria-selected=true]{background:var(--markee-selected);color:var(--markee-selected-text);font-weight:650}
28
+ .content{padding:20px 24px;overflow-y:auto;overflow-x:hidden;min-height:0;overscroll-behavior:contain}
29
+ .row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
30
+ .between{justify-content:space-between}
31
+ .stack{display:grid;gap:12px}
32
+ .btn{border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);background:var(--markee-surface);color:var(--markee-text);padding:10px 15px;font-weight:600;font-size:13px}
33
+ .primary{background:var(--markee-primary);border-color:var(--markee-primary);color:var(--markee-primary-text)}
34
+ .accent{background:var(--markee-accent);border-color:var(--markee-accent);color:var(--markee-accent-text)}
35
+ .danger{color:var(--markee-error-text)}
36
+ .full{width:100%}
37
+ .card{border:1px solid var(--markee-border);border-radius:var(--markee-card-radius);background:var(--markee-surface);padding:17px 18px}
38
+ .winner{background:var(--markee-accent-surface);border-color:var(--markee-accent-border)}
39
+ .message{font-family:ui-monospace,monospace;font-size:15px;overflow-wrap:anywhere;white-space:pre-wrap;line-height:1.5;margin:10px 0}
40
+ .name{font-size:12px;color:var(--markee-muted)}
41
+ .meta{font-size:12px;color:var(--markee-muted)}
42
+ .stat{display:flex;justify-content:space-between;gap:16px;margin:8px 0;font-size:13px}
43
+ .stat strong{text-align:right;overflow-wrap:anywhere}
44
+ .grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
45
+ .metric{font-size:23px;font-weight:650;letter-spacing:-.6px}
46
+ .note,.error,.success{padding:12px 14px;border-radius:var(--markee-control-radius);font-size:13px;overflow-wrap:anywhere;margin-bottom:14px}
47
+ .note{background:var(--markee-info);color:var(--markee-info-text)}
48
+ .error{background:var(--markee-error);color:var(--markee-error-text)}
49
+ .success{background:var(--markee-success);color:var(--markee-success-text)}
50
+ label{display:block;font-size:13px;font-weight:600;margin-bottom:6px}
51
+ input,textarea,select{display:block;width:100%;border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);background:var(--markee-input-background);color:var(--markee-input-text);padding:12px;min-width:0}
52
+ input::placeholder,textarea::placeholder{color:var(--markee-muted);opacity:1}
53
+ textarea{font-family:ui-monospace,monospace;font-size:14px;min-height:88px;max-height:220px;resize:vertical}
54
+ form{display:grid;gap:16px}
55
+ .field small{display:block;margin-top:5px;font-weight:400;font-size:12px}
56
+ .divider{height:1px;background:var(--markee-border);margin:20px 0}
57
+ a{color:var(--markee-link);text-decoration:none;overflow-wrap:anywhere}
58
+ ul{padding-left:20px;font-size:14px}
59
+ .review li{margin-bottom:9px;overflow-wrap:anywhere}
60
+ .loading{padding:40px;text-align:center;color:var(--markee-muted)}
61
+ .empty{padding:24px 8px;text-align:center}
62
+ .spinner{display:inline-block;width:14px;height:14px;border-radius:100%;border:2px solid var(--markee-border);border-top-color:var(--markee-muted);animation:spin 1s linear infinite;margin-right:8px}
63
+ @keyframes spin{to{transform:rotate(360deg)}
64
+ }
65
+ footer{flex-shrink:0;padding:14px 20px;border-top:1px solid var(--markee-border);background:var(--markee-background);color:var(--markee-muted);font-size:11px}
66
+ .mono{font-family:ui-monospace,monospace}
67
+ .review{margin:16px 0}.review ul{padding-left:18px}.review .stat{border-top:1px solid var(--markee-border);padding-top:10px}.review-title{font-size:14px;color:var(--markee-muted);font-weight:500}
68
+ .wallets{display:grid;gap:10px}
69
+
70
+
71
+ .header-caption{font-size:12px;color:var(--markee-muted);margin:5px 0 0}
72
+ .close{line-height:1;padding:4px 6px;flex-shrink:0}
73
+ .text-button{border:0;background:transparent;color:var(--markee-link);padding:0;font-size:12px;font-weight:500;text-align:left}
74
+ .compact{padding:5px 9px;font-size:11px;line-height:1.4}
75
+ .leaderboard{border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);padding:12px 14px;background:var(--markee-surface);margin-bottom:20px}
76
+ .leaderboard>.row{gap:8px}.leaderboard a{font-size:11px}.leaderboard .text-button{color:var(--markee-muted)}.chevron{display:inline-block;width:15px;font-size:16px}
77
+ .leaderboard-list{display:grid;gap:10px;margin-top:12px;padding-top:12px;border-top:1px solid var(--markee-border)}
78
+ .leaderboard-entry{display:flex;gap:12px;align-items:center;justify-content:space-between;border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);padding:10px}
79
+ .selected-entry{border-color:var(--markee-link)}.leaderboard-entry .message{font-size:12px;line-height:1.5;margin:0}.entry-caption{gap:5px;margin-top:5px}.entry-caption:empty{display:none}.entry-caption .pill{font-size:9px}.entry-caption .name,.entry-caption .meta{font-size:10px}
80
+ .entry-funding{display:flex;flex-direction:column;align-items:flex-end;gap:4px;flex:0 0 auto;max-width:44%;text-align:right;overflow-wrap:anywhere}.entry-funding .meta{font-size:10px}
81
+ .message-card{padding:16px;border:1px solid var(--markee-border);border-radius:var(--markee-card-radius);background:var(--markee-surface);min-width:0}
82
+ .message-card.winner{background:var(--markee-accent-surface);border-color:var(--markee-accent-border)}
83
+ .message-card>.message{font-size:17px;margin:12px 0 6px}.message-card .eyebrow{font-size:10px}.message-card>.name{margin:4px 0}
84
+ .message-tools{margin-top:12px;gap:10px 16px}.message-tools .text-button{font-size:11px}.editor-card{display:grid;gap:16px}
85
+ .field label{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.field-count{font-size:10px;font-weight:400;color:var(--markee-muted)}.field small{font-size:10px}
86
+ .message-stats{border-top:1px solid var(--markee-border);margin-top:16px;padding-top:12px}.message-stats>.meta{font-size:11px}.stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:8px}.stats-grid>div{min-width:0}.stats-grid strong{display:block;font-family:ui-monospace,monospace;font-size:12px;overflow-wrap:anywhere}.stat-label{display:block;font-size:9px;letter-spacing:.6px;text-transform:uppercase;color:var(--markee-muted);margin-bottom:4px}
87
+ .funding-card{border:1px solid var(--markee-link);border-radius:var(--markee-card-radius);background:var(--markee-surface);padding:16px;min-width:0}
88
+ .funding-card>label{font-size:11px;color:var(--markee-muted);font-weight:500;margin-bottom:8px}.amount-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.amount-value{display:flex;gap:6px;align-items:baseline;min-width:0;flex:1 1 140px}.amount-value input{background:transparent;border:0;border-radius:2px;box-shadow:none;font-family:ui-monospace,monospace;font-size:23px;font-weight:600;min-width:0;width:100%;padding:3px 0;color:var(--markee-text)}.amount-value>span{font-size:11px;color:var(--markee-muted);white-space:nowrap}.amount-presets{display:flex;gap:6px;margin-left:auto}.amount-presets button{color:var(--markee-link)}.amount-presets button[aria-pressed=true]{border-color:var(--markee-link);background:var(--markee-selected);color:var(--markee-selected-text)}
89
+ .funding-caption{display:flex;justify-content:space-between;flex-wrap:wrap;gap:4px 10px;margin-top:9px;color:var(--markee-muted);font-size:10px}.funding-tools{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;border-top:1px solid var(--markee-border);margin-top:13px;padding-top:12px}.funding-tools .meta{font-size:10px}.funding-hint{font-size:11px;margin:0}.funding-hint+.note{margin:0;font-size:11px}
90
+ .footer-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.footer-actions>.primary:last-child{margin-left:auto}.footer-actions:empty{display:none}.footer-meta{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px;margin-top:10px;font-size:10px}.footer-meta .text-button{font-size:10px}
91
+
92
+ @media(max-width:560px){.overlay{padding:8px;align-items:flex-end}
93
+ .panel{border-radius:var(--markee-radius) var(--markee-radius) var(--markee-control-radius) var(--markee-control-radius);max-height:94dvh}
94
+ .content{padding:20px 18px}
95
+ header{padding:20px 18px}
96
+ .nav{padding-left:10px;padding-right:10px}
97
+ .grid{grid-template-columns:1fr 1fr}
98
+ .metric{font-size:19px}
99
+ footer{padding:12px 18px}
100
+ .btn{padding:10px 12px}
101
+ .compact{padding:5px 9px}.text-button{padding:0}.footer-actions>.btn{flex:1;white-space:normal}.footer-actions>.primary:last-child{margin-left:0}
102
+ }
103
+
104
+ @media(prefers-reduced-motion:reduce){*{animation:none!important;scroll-behavior:auto!important}
105
+ }
106
+
107
+ `;var D={background:"#fbfcff",surface:"#ffffff",text:"#181b21",muted:"#687080",border:"#dce1ea",inputBackground:"#ffffff",inputText:"#202737",primary:"#282e3e",primaryText:"#ffffff",accent:"#c9f1a7",accentText:"#2b3c20",accentSurface:"#f1f7ea",accentSurfaceText:"#4e6535",accentBorder:"#cde3b5",selected:"#e9edf7",selectedText:"#242c47",info:"#edf1f9",infoText:"#52617b",error:"#fff0f0",errorText:"#9c3540",success:"#edf7e6",successText:"#40602f",link:"#4762a5",focusRing:"#5871c8",overlay:"#17203888"},q={background:"#151923",surface:"#202633",text:"#f0f3f8",muted:"#a6b1c3",border:"#434f63",inputBackground:"#10151e",inputText:"#f0f3f8",primary:"#dde6fa",primaryText:"#182238",accent:"#c9f1a7",accentText:"#24361a",accentSurface:"#243221",accentSurfaceText:"#c4e5ad",accentBorder:"#526b40",selected:"#313d53",selectedText:"#edf3ff",info:"#243149",infoText:"#bdcff1",error:"#44262e",errorText:"#ffbdc5",success:"#233b2b",successText:"#b5e4bf",link:"#adc5ff",focusRing:"#a1b4ff",overlay:"#030711b8"},W='Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',U=n=>`--markee-${n.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}`;function R(n,e){let t,a=(i={})=>{t?.(),t=void 0;let r=i.mode==="system"?window.matchMedia("(prefers-color-scheme: dark)"):void 0,s=()=>{let d=i.mode==="dark"||r?.matches?"dark":"light",c=d==="dark"?q:D;n.dataset.markeeTheme=d,n.style.setProperty("--markee-color-scheme",d);for(let u of Object.keys(c)){let w=i.colors?.[u];n.style.setProperty(U(u),w&&CSS.supports("color",w)?w:c[u])}n.style.setProperty("--markee-font-family",i.fontFamily&&CSS.supports("font-family",i.fontFamily)?i.fontFamily:W);let f=typeof i.radius=="number"&&Number.isFinite(i.radius)?Math.max(0,Math.min(48,i.radius)):22;n.style.setProperty("--markee-radius",`${f}px`),n.style.setProperty("--markee-card-radius",`${f*.64}px`),n.style.setProperty("--markee-control-radius",`${f*.45}px`),n.style.setProperty("--markee-small-radius",`${f*.32}px`)};s(),r&&(r.addEventListener("change",s),t=()=>r.removeEventListener("change",s))};return a(e),{set:a,destroy(){t?.(),t=void 0}}}var g=2628000n,k=10n**15n,C=(...n)=>n.reduce((e,t)=>e>t?e:t,0n);function S(n,e){let t=n.strategy==="rent",a=t?(BigInt(n.minimum)+k-1n)/k*k:e?1n:BigInt(n.minimum),i=n.entries.filter(u=>u.address.toLowerCase()!==e?.address.toLowerCase()),r=C(...i.map(u=>BigInt(u.score))),s=!!e&&e.address.toLowerCase()===n.top?.toLowerCase();if(!t){let u=BigInt(e?.totalFunds??"0");return{minimum:a,boost:C(a,r+k-u,s?u:0n),double:s&&u>0n}}let c=!!e&&n.wallet?.backedMarkee?.toLowerCase()===e.address.toLowerCase()?BigInt(n.wallet.flowRate):0n,f=C(BigInt(e?.aggregateRate??"0")-c);return{minimum:a,boost:C(a,(r-f)*g+k,s?c*g*2n:0n),double:s&&c>0n}}var A=n=>{let e=BigInt(n);return`${e/10n**18n}.${(e%10n**18n).toString().padStart(18,"0")}`.replace(/\.?0+$/,"")||"0"},o=(n,e,t="",a="")=>`<button type="button" class="btn ${t}" data-cmd="${n}" ${a}>${e}</button>`,L=(n,e)=>/^https:\/\//.test(n)?`<a href="${l(n)}" target="_blank" rel="noopener noreferrer">${l(e)} \u2197</a>`:"",z=class{constructor(e,t){this.client=e;this.options=t;if(typeof document>"u")throw new Error("Open the Markee modal in a browser.");this.previousFocus=document.activeElement,this.previousOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.host=document.createElement("div"),this.host.setAttribute("data-markee-modal",""),this.root=this.host.attachShadow({mode:"open"}),this.theme=R(this.host,t.theme??e.options.theme),document.body.appendChild(this.host),this.root.addEventListener("click",this.onClick),this.root.addEventListener("submit",this.onSubmit),this.root.addEventListener("keydown",this.onKey),this.root.addEventListener("input",()=>{this.captureDraft(),this.updateFields()}),this.render(),this.root.querySelector('[data-cmd="close"]')?.focus(),this.reload(),this.watchAccount()}host;root;theme;snapshot;account=null;tab="main";leaderboardOpen=!1;initialSelection=!1;renderedView="";form;review;authorization;adminResult;adminProcessing=!1;result;pending;wallets;busy=!1;error="";closed=!1;epoch=0;timer;accountTimer;watched;previousFocus;previousOverflow;abort=new AbortController;drafts=new Map;viewObserver;viewed=new Set;walletChanged=()=>{this.initialSelection=!1,this.review=void 0,this.authorization=void 0,this.form=void 0,this.result=void 0,this.error="Wallet changed. Refreshing this placement\u2026",this.reload()};close(){this.closed||(this.closed=!0,this.abort.abort(),clearTimeout(this.timer),clearTimeout(this.accountTimer),this.viewObserver?.disconnect(),this.theme.destroy(),this.watched?.removeListener?.("accountsChanged",this.walletChanged),this.watched?.removeListener?.("chainChanged",this.walletChanged),this.host.remove(),document.body.style.overflow=this.previousOverflow,this.previousFocus?.focus(),this.options.onClose?.())}setTheme(e){this.closed||this.theme.set(e)}async watchAccount(){try{let e=await this.client.provider();e!==this.watched&&(this.watched?.removeListener?.("accountsChanged",this.walletChanged),this.watched?.removeListener?.("chainChanged",this.walletChanged),this.watched=e||void 0,e?.on?.("accountsChanged",this.walletChanged),e?.on?.("chainChanged",this.walletChanged)),(await this.client.account())?.toLowerCase()!==this.account?.toLowerCase()&&!this.busy&&this.walletChanged()}catch{}this.closed||(this.accountTimer=setTimeout(()=>void this.watchAccount(),2e3))}async reload(e){let t=++this.epoch;try{let a=await this.client.account(),i=await this.client.snapshot(this.options.placement,a,e,this.abort.signal);if(this.closed||t!==this.epoch)return;if(this.account=a,this.snapshot=i,this.error="",this.form&&(this.form.entry=i.entries.find(s=>s.address.toLowerCase()===this.form.entry.address.toLowerCase())??this.form.entry),!this.initialSelection&&a){this.initialSelection=!0;let s=i.entries.find(d=>d.address.toLowerCase()===i.wallet?.backedMarkee?.toLowerCase())??i.entries.find(d=>d.owner.toLowerCase()===a.toLowerCase());s&&!this.form&&!this.drafts.get("create")?.message&&(this.form={kind:i.strategy==="rent"?"stream":"fund",entry:s})}this.options.placement.kind==="gardens-community"&&(this.adminResult=this.client.communityReceipt(this.options.placement)||void 0),this.pending=a?this.client.pending(this.options.placement,a):void 0;let r=this.client.pendingAuthorization();this.adminProcessing=!!r&&this.options.placement.kind==="gardens-community"&&r.account.toLowerCase()===a?.toLowerCase()&&r.community.toLowerCase()===this.options.placement.community.toLowerCase()&&r.chainId===this.options.placement.chainId,this.render(),this.pending&&this.poll()}catch(a){!this.closed&&t===this.epoch&&this.fail(a)}}fail(e){this.error=e instanceof Error?e.message:String(e),this.render()}async task(e){if(!this.busy){this.busy=!0,this.error="",this.render();try{await e()}catch(t){this.fail(t)}finally{this.busy=!1,this.render()}}}async connect(){if(!this.client.options.wallet){let e=await P();if(e.length>1){this.wallets=e,this.render();return}}this.host.style.visibility="hidden";try{await this.client.connect()}finally{this.host.style.visibility=""}await this.reload()}async prepare(e){if(this.pending)throw new Error("Check your pending transaction before preparing another action.");if(!this.account){await this.connect();return}this.review=await this.client.prepare(this.options.placement,e),this.authorization=void 0}async poll(){if(clearTimeout(this.timer),!(!this.pending||this.closed)){try{let e=this.pending,t=await this.client.verify(e);if(this.closed||this.pending?.hash!==e.hash)return;if(t.status!=="pending"){if(this.pending=void 0,this.result=t,this.error="",await this.reload(),t.createdMarkee&&this.snapshot?.strategy==="rent"){let a=this.snapshot.entries.find(r=>r.address.toLowerCase()===t.createdMarkee.toLowerCase()),i=e.preparation.action;if(i.kind==="create"){this.form={kind:"stream",entry:a||{address:t.createdMarkee,owner:e.preparation.transaction.from,message:i.message,name:i.name,score:"0",totalFunds:"0",aggregateRate:"0",legacyFloor:"0",moderated:!1,views:0}};let r=this.drafts.get("create")?.amount;r&&this.drafts.set(this.draftKey(),{amount:r})}}this.render();return}}catch(e){this.closed||this.fail(e)}!this.closed&&this.pending&&(this.timer=setTimeout(()=>void this.poll(),5e3))}}async applyAdmin(e){if(this.authorization=void 0,this.adminProcessing=e.status==="working"||e.status==="submitted",e.status==="complete"&&(this.adminResult=e.result,await this.reload()),e.status==="failed")throw new Error(e.error||"Community action failed.")}onClick=e=>{let t=e.target.closest("[data-cmd]");if(!t)return;let a=t.dataset.cmd;if(a==="close"){this.close();return}if(!this.busy){if(this.captureDraft(),a==="leaderboard"){this.leaderboardOpen=!this.leaderboardOpen,this.render();return}if(a==="create"){this.tab="main",this.form=void 0,this.review=void 0,this.render();return}if(a.startsWith("tab:")){this.tab=a.slice(4),this.review=void 0,this.authorization=void 0,this.render();return}if(a==="back"){this.review||this.authorization||this.wallets?(this.review=void 0,this.authorization=void 0,this.wallets=void 0):this.tab!=="main"?this.tab="main":this.form?.kind.startsWith("edit")?this.form={kind:this.snapshot.strategy==="rent"?"stream":"fund",entry:this.form.entry}:this.form=void 0,this.render();return}if(a.startsWith("preset:")&&this.snapshot){let i=this.root.querySelector("#amount");if(i){let r=S(this.snapshot,this.form?.entry);i.value=A((a==="preset:min"?r.minimum:r.boost).toString()),this.captureDraft(),this.updateFields()}return}if(a.startsWith("form:")){let[,i,r]=a.split(":"),s=this.snapshot?.entries.find(d=>d.address===r);s&&(this.form={kind:i,entry:s},this.tab="main"),this.render();return}this.task(async()=>{if(a==="connect")await this.connect();else if(a.startsWith("wallet:"))await this.client.connect(this.wallets[Number(a.slice(7))].provider),this.wallets=void 0,await this.reload();else if(a==="refresh")await this.reload();else if(a==="more")await this.reload(this.snapshot?.nextOffset??void 0);else if(a==="send"&&this.review){let i=this.review,r=await this.client.send(i);this.pending={preparation:i,hash:r},this.review=void 0,this.form=void 0,this.result=void 0,this.poll()}else if(a==="check")await this.poll();else if(["stop","withdraw-buffer","settle"].includes(a))await this.prepare({kind:a});else if(a.startsWith("claim-refund:")||a.startsWith("connect-refund:")||a.startsWith("claim-top:")){let[i,r]=a.split(":");await this.prepare({kind:i,markee:r})}else if(a==="activate"||a==="claim"){let i=this.options.placement;if(i.kind!=="gardens-community"||!this.account)return;let r=this.account;this.authorization={kind:a,account:r,value:await this.client.authorize(i,a)},this.review=void 0}else if(a==="sign"&&this.authorization){let i=this.authorization;try{await this.applyAdmin(await this.client.executeAuthorization(i.kind,i.value,i.account))}finally{this.client.pendingAuthorization()&&(this.authorization=void 0,this.adminProcessing=!0)}}else if(a==="admin-check")await this.applyAdmin(await this.client.resumeAuthorization());else if(a==="bridge"&&this.adminResult){let i=await this.client.bridgeStatus(this.adminResult.bridgeProtocol||"none",this.adminResult.transactionHash,this.snapshot.chainId,this.options.placement.chainId);this.error=`Treasury transfer status: ${i.status.replace(/_/g," ")}.`}})}};onSubmit=e=>{if(e.preventDefault(),this.busy)return;this.captureDraft();let t=e.target,a=new FormData(t),i=r=>String(a.get(r)||"");this.task(async()=>{if(t.dataset.form==="create")this.snapshot?.strategy==="rent"&&v(i("amount")),await this.prepare({kind:"create",message:i("message"),name:i("name"),...this.snapshot?.strategy==="sale"?{amount:v(i("amount"),!0)}:{}});else if(t.dataset.form==="balance")await this.prepare({kind:i("kind"),amount:v(i("amount"))});else if(this.form){let{kind:r,entry:s}=this.form;r==="fund"?await this.prepare({kind:r,markee:s.address,amount:v(i("amount"))}):r==="stream"?await this.prepare({kind:r,markee:s.address,monthlyAmount:v(i("amount"))}):r==="edit-message"?await this.prepare({kind:r,markee:s.address,message:i("message")}):await this.prepare({kind:r,markee:s.address,name:i("name")})}})};draftKey(){return this.tab!=="main"?this.tab:this.form?`${this.form.kind}:${this.form.entry.address}`:"create"}captureDraft(){let e=this.root.querySelector("form");e&&this.drafts.set(this.draftKey(),Object.fromEntries([...new FormData(e)].map(([t,a])=>[t,String(a)])))}onKey=e=>{let t=e;if(t.key==="Escape"&&(t.preventDefault(),this.close()),t.key!=="Tab"||this.host.style.visibility==="hidden")return;let a=[...this.root.querySelectorAll("button:not(:disabled),a[href],input,textarea,select")],i=a[0],r=a[a.length-1];t.shiftKey&&this.root.activeElement===i?(t.preventDefault(),r?.focus()):!t.shiftKey&&this.root.activeElement===r&&(t.preventDefault(),i?.focus())};field(e,t,a="",i="",r=!1){let s=e==="message"?this.snapshot?.maxMessageBytes:e==="name"?this.snapshot?.maxNameBytes:void 0,d=`${e==="name"?"":"required"} ${s===void 0?"":`data-byte-limit="${s}"`} aria-describedby="${e}-hint"`;return`<div class="field"><label for="${e}">${t}${s===void 0?"":`<span class="field-count" aria-hidden="true" data-count-for="${e}"></span>`}</label>${r?`<textarea id="${e}" name="${e}" ${d} placeholder="Your message on the sign">${l(a)}</textarea>`:`<input id="${e}" name="${e}" ${e==="amount"?'inputmode="decimal"':""} value="${l(a)}" ${d}>`}${i?`<small id="${e}-hint">${i}</small>`:""}</div>`}reviewHtml(){let e=this.review,t=e.action,a=t.kind==="create"||t.kind==="edit-message"?t.message:this.form?this.entryMessage(this.form.entry):void 0,i=e.review.lines.filter(r=>!r.startsWith("Message: "));return`<h2 class="review-title">${l(e.review.title)}</h2>${a?`<section class="message-card"><p class="message">${l(a)}</p></section>`:""}<section class="review"><ul>${i.map(r=>`<li>${l(r)}</li>`).join("")}</ul><div class="stat"><span>Estimated network fee</span><strong>${m(e.review.estimatedGasCost)} ETH</strong></div><div class="stat"><span>Wallet</span><strong class="mono">${E(e.transaction.from)}</strong></div></section><p class="note">${l(e.review.reward)}</p><p class="meta">This review expires in 5 minutes. Your wallet shows the final network fee.</p>`}authorizationHtml(){let e=this.authorization,t=e.value.typedData.message;return`<div class="eyebrow">Community authorization</div><h2>${e.kind==="activate"?"Activate this community\u2019s Markee":"Claim community revenue"}</h2><p>A signed authorization lets Markee\u2019s keeper execute this action. The server rechecks your current council role before submitting it.</p><div class="card stack">${["registryCommunity","recipient","leaderboardFactory"].filter(a=>t[a]).map(a=>`<div class="meta">${l(a)}<br><strong class="mono">${l(t[a])}</strong></div>`).join("")}${["claimAmount","gasCost","maxFeeAmount"].filter(a=>t[a]!==void 0).map(a=>`<div class="stat"><span>${a==="claimAmount"?"Revenue":a==="gasCost"?"Keeper network fee":"Maximum bridge fee"}</span><strong>${m(String(t[a]))} ETH</strong></div>`).join("")}</div><p class="note">${e.kind==="claim"?"Revenue is sent to the community council treasury. Bridged claims remain pending until destination delivery is confirmed.":"This creates the community board and its revenue vault on the Markee chain."}</p>`}entryMessage(e){return e.moderated===!1?e.message:e.moderated?"This message is hidden by moderation.":"Message unavailable while moderation loads."}entriesHtml(){let e=this.snapshot,t=[...e.entries].sort((r,s)=>BigInt(r.score)>BigInt(s.score)?-1:BigInt(r.score)<BigInt(s.score)?1:0),a=[...t.filter(r=>r.owner.toLowerCase()===this.account?.toLowerCase()),...t.filter(r=>r.owner.toLowerCase()!==this.account?.toLowerCase())],i=e.board&&[8453,11155111].includes(e.chainId)?`https://${e.chainId===8453?"www":"2staging"}.markee.xyz/markee/${e.board}`:"";return`<section class="leaderboard" aria-label="Markee leaderboard"><div class="row between">${o("leaderboard",`<span class="chevron" aria-hidden="true">${this.leaderboardOpen?"\u2303":"\u2304"}</span> Leaderboard`,"text-button",`aria-expanded="${this.leaderboardOpen}" aria-controls="markee-leaderboard"`)}${L(i,"Open in Markee")}</div>${this.leaderboardOpen?`<div class="leaderboard-list" id="markee-leaderboard">${a.map(r=>{let s=r.owner.toLowerCase()===this.account?.toLowerCase(),d=r.address.toLowerCase()===e.top?.toLowerCase(),c=r.address.toLowerCase()===this.form?.entry.address.toLowerCase();return`<article class="leaderboard-entry ${d?"winner":""} ${c?"selected-entry":""}"><div class="grow"><p class="message">${l(this.entryMessage(r))}</p><div class="row entry-caption">${d?'<span class="pill">Winner</span>':""}${s?'<span class="meta">Yours</span>':""}${r.moderated===!1&&r.name?`<span class="name">${l(r.name)}</span>`:""}</div></div><div class="entry-funding"><div class="meta mono">${e.strategy==="rent"?`${m((BigInt(r.score)*g).toString())} ETH/mo`:`${m(r.totalFunds)} ETH`}</div><span class="meta">${r.views===null?"Views unavailable":`${r.views.toLocaleString()} views`}</span>${o(`form:${e.strategy==="sale"?"fund":"stream"}:${r.address}`,c?"Selected":"Fund",`compact ${c?"primary":""}`,`aria-pressed="${c}" aria-label="${c?"Selected":"Fund"} message ${E(r.address)}"`)}</div></article>`}).join("")||'<p class="empty">No Markees yet. Create the first one.</p>'}${e.strategy==="rent"&&e.rankedTop&&e.rankedTop.toLowerCase()!==e.top?.toLowerCase()?`<p class="note">The ranking has changed. ${o(`claim-top:${e.rankedTop}`,"Refresh winner","compact")}</p>`:""}${e.nextOffset!==null?o("more","Browse more messages","full"):""}</div>`:""}</section>`}selectedMessageHtml(e){let t=this.snapshot,a=t.wallet,i=e.owner.toLowerCase()===this.account?.toLowerCase(),r=t.entries.find(c=>c.owner.toLowerCase()===this.account?.toLowerCase()),s=e.address.toLowerCase()===t.top?.toLowerCase(),d=e.address.toLowerCase()===a?.backedMarkee?.toLowerCase()&&BigInt(a?.flowRate??"0")>0n;return`<section class="message-card ${s?"winner":""}"><div class="row between"><span class="eyebrow">${i?"Your Markee":"Markee you\u2019re funding"}</span>${s?'<span class="pill">Winner</span>':""}</div><p class="message">${l(this.entryMessage(e))}</p>${e.moderated===!1&&e.name?`<p class="name">${l(e.name)}</p>`:""}<div class="row message-tools">${i?o(`form:edit-message:${e.address}`,"Edit message","text-button")+o(`form:edit-name:${e.address}`,"Edit name","text-button"):""}${!i&&r?o(`form:${t.strategy==="rent"?"stream":"fund"}:${r.address}`,"Back to your Markee","text-button"):""}${o("create","Create your own Markee","text-button")}${t.strategy==="rent"&&this.account?o(`claim-refund:${e.address}`,"Collect refund","text-button"):""}</div><div class="message-stats">${t.strategy==="rent"?`<span class="meta">${d?s?"\u25CF Active stream":a?.poolConnected?"\u25CF Stream refunded while not the winner":"\u25CF Connect your refund pool in Deposit manager":"No active stream from your wallet to this message"}</span><div class="stats-grid"><div><span class="stat-label">Ranking rate</span><strong>${m((BigInt(e.score)*g).toString())} ETH/mo</strong></div><div><span class="stat-label">Your rate</span><strong>${m((d?BigInt(a.flowRate)*g:0n).toString())} ETH/mo</strong></div>${a?`<div><span class="stat-label">ETHx balance</span><strong>${m(a.ethx)} ETHx</strong></div><div><span class="stat-label">Funds remaining</span><strong title="Across all of your ETHx flows">${a.runwaySeconds===null?"No net outflow":`${(Number(a.runwaySeconds)/86400).toFixed(1)} days`}</strong></div>`:""}</div>${BigInt(e.legacyFloor)>0n?`<p class="meta">Ranking includes a decaying legacy floor of ${m((BigInt(e.legacyFloor)*g).toString())} ETH/mo.</p>`:""}`:`<div class="stats-grid"><div><span class="stat-label">Total funded</span><strong>${m(e.totalFunds)} ETH</strong></div><div><span class="stat-label">Views</span><strong>${e.views===null?"Unavailable":e.views.toLocaleString()}</strong></div></div>`}</div></section>`}fundingHtml(e){let t=this.snapshot,a=t.wallet,i=t.strategy==="rent",r=S(t,e),s=!!e&&e.address.toLowerCase()===a?.backedMarkee?.toLowerCase(),d=i&&s?BigInt(a.flowRate)*g:0n,c=BigInt(t.minimum)>r.minimum?BigInt(t.minimum):r.minimum,f=d>c?d:c,u=!i&&e?1n:BigInt(t.minimum);return`<section class="funding-card"><label for="amount">${i?"Monthly streaming rate":"Payment amount"}</label><div class="amount-row"><div class="amount-value"><input id="amount" name="amount" inputmode="decimal" value="${l(A(f.toString()))}" required aria-describedby="amount-hint"><span class="mono">${i?"ETHx/mo":"ETH"}</span></div><div class="amount-presets">${o("preset:min","MIN","compact mono",'title="Use the minimum amount"')}${o("preset:boost",r.double?"2\xD7":"WIN","compact mono",'title="Suggest an amount using the loaded ranking"')}</div></div><div class="funding-caption"><span id="amount-hint">Minimum ${A(u.toString())} ${i?"ETH/mo":"ETH"}</span><span>${a?`Balance ${m(a.eth)} ETH`:"Connect to see your balance"}</span></div><div class="funding-tools">${o("tab:wallet",i?"Deposit manager \u2192":"Wallet & rewards \u2192","text-button")}<span class="meta">${i?a?.runwaySeconds!=null?`${(Number(a.runwaySeconds)/86400).toFixed(1)} days remaining`:a?`${m(a.ethx)} ETHx available`:"ETH funds your ETHx balance":"One-time payment"}</span></div></section><p class="meta funding-hint">${i?"Unspent rent is refunded while your message is not the winner. You can stop streaming at any time.":e?"Adding funds keeps the current message owner.":"Your connected wallet will own this message."} Shortcuts use the loaded ranking; positions can change.</p>`}formHtml(){let e=this.snapshot,t=this.form;return t?.kind.startsWith("edit")?`<form id="markee-action" data-form="entry"><section class="message-card"><div class="eyebrow">Your Markee</div>${t.kind==="edit-message"?this.field("message","Markee message",t.entry.message,`${e.maxMessageBytes} UTF-8 bytes maximum.`,!0):this.field("name","Your name (optional)",t.entry.name,`${e.maxNameBytes} UTF-8 bytes maximum.`)}</section></form>`:`<form id="markee-action" data-form="${t?"entry":"create"}">${t?this.selectedMessageHtml(t.entry):`<section class="message-card editor-card">${this.field("message","Markee message","",`${e.maxMessageBytes} UTF-8 bytes maximum.`,!0)}${this.field("name","Your name (optional)","",`${e.maxNameBytes} UTF-8 bytes maximum.`)}</section>`}${this.fundingHtml(t?.entry)}${!t&&e.strategy==="rent"?'<p class="note">First create your message, then review and confirm its stream. Your chosen rate is kept for the next step.</p>':""}</form>`}updateFields(){for(let t of this.root.querySelectorAll("[data-byte-limit]")){let a=Number(t.dataset.byteLimit),i=new TextEncoder().encode(t.value).length;t.setCustomValidity(i>a?`Use at most ${a} UTF-8 bytes.`:"");let r=this.root.querySelector(`[data-count-for="${t.name}"]`);r&&(r.textContent=`${i}/${a}`)}let e=this.root.querySelector("#markee-action #amount");if(e&&this.snapshot){let t=S(this.snapshot,this.form?.entry),a;try{a=BigInt(v(e.value,this.snapshot.strategy==="sale"))}catch{}let i=this.snapshot.strategy==="sale"&&this.form?1n:BigInt(this.snapshot.minimum);e.setCustomValidity(a===void 0?"Enter a valid ETH amount.":a<i?`Use at least ${A(i.toString())} ETH${this.snapshot.strategy==="rent"?" per month":""}.`:""),this.root.querySelector('[data-cmd="preset:min"]')?.setAttribute("aria-pressed",String(a===t.minimum)),this.root.querySelector('[data-cmd="preset:boost"]')?.setAttribute("aria-pressed",String(a===t.boost))}}walletHtml(){let e=this.snapshot,t=e.wallet;if(!t)return'<p class="empty">Connect your wallet to see balances and manage your Markee.</p>'+o("connect","Connect app wallet","primary full");let a=`<h2>Your wallet</h2><p class="meta mono">${l(t.address)}</p><div class="grid"><div class="card"><span class="meta">Native ETH</span><div class="metric">${m(t.eth)}</div></div><div class="card"><span class="meta">MARKEE</span><div class="metric">${t.markee===null?"Unavailable":m(t.markee,2)}</div></div></div>`;return e.strategy==="sale"?a+'<p class="note">Payments reward the paying wallet when this board has RevNet enabled. Manage your messages from the leaderboard.</p>':(a+=`<div class="divider"></div><div class="card"><div class="stat"><span>Available ETHx</span><strong>${m(t.ethx)} ETHx</strong></div><div class="stat"><span>This board\u2019s monthly rate</span><strong>${m((BigInt(t.flowRate)*2628000n).toString())} ETH</strong></div><div class="stat"><span>Refund flow / month</span><strong>${m((BigInt(t.refundRate)*2628000n).toString())} ETHx</strong></div><div class="stat"><span>Runway across all ETHx flows</span><strong>${t.runwaySeconds===null?"No net outflow":`${(Number(t.runwaySeconds)/86400).toFixed(1)} days`}</strong></div><p class="meta">Snapshot at ${new Date(t.observedAt*1e3).toLocaleTimeString()}. Balances and rates can change.</p><div class="row">${BigInt(t.flowRate)>0n?o("stop","Stop stream","danger"):""}${t.backedMarkee&&!t.poolConnected?o(`connect-refund:${t.backedMarkee}`,"Connect refund pool","accent"):""}${o("refresh","Refresh balances")}</div></div><div class="divider"></div><h3>Manage ETHx</h3><form data-form="balance"><div class="grid"><div><label for="kind">Action</label><select id="kind" name="kind"><option value="wrap">Deposit ETH \u2192 ETHx</option><option value="unwrap">Withdraw ETHx \u2192 ETH</option></select></div>${this.field("amount","Amount","0.001")}</div><button type="submit" class="btn">Review balance change</button></form><div class="divider"></div><div class="card"><h3>Buffer and rewards</h3><div class="stat"><span>Refundable board buffer</span><strong>${m(t.withdrawableDeposit)} ETHx</strong></div><div class="stat"><span>Accrued rent to settle</span><strong>${m(t.pendingSettlement)} ETH</strong></div><p class="meta">Rewards come from actual rent spent, not the ETH deposited into your wallet.</p><div class="row">${BigInt(t.withdrawableDeposit)>0n?o("withdraw-buffer","Recover buffer"):""}${BigInt(t.pendingSettlement)>0n?o("settle",e.revnetEnabled?"Claim MARKEE":"Settle rent","primary"):""}${t.backedMarkee&&BigInt(t.poolClaimable)>0n?o(`claim-refund:${t.backedMarkee}`,"Collect pool refund"):""}</div></div>`,a)}communityHtml(){let e=this.snapshot.community;return e?`<h2>Community treasury</h2><p class="muted">Community council members can activate Markee and collect community revenue.</p><div class="card"><div class="stat"><span>Integration</span><strong>${e.integration.status==="active"?"Active":"Not activated"}</strong></div><div class="stat"><span>Available revenue</span><strong>${m(e.revenue.claimableAmount)} ETH</strong></div>${e.permissions?.councilSafe?`<p class="meta">Council treasury<br><span class="mono">${l(e.permissions.councilSafe)}</span></p>`:""}<div class="row">${this.adminProcessing?'<p class="note">A signed community action is processing. Check it before authorizing another.</p>'+o("admin-check","Check community action","primary"):this.account?e.integration.status!=="active"&&e.permissions?.canActivate?o("activate","Activate community Markee","primary"):e.permissions?.canClaim&&BigInt(e.revenue.claimableAmount)>0n?o("claim","Review revenue claim","primary"):'<span class="meta">No authorized action is currently available for this wallet.</span>':o("connect","Connect council wallet","primary")}</div></div>${this.adminResult?`<div class="success">Source transaction confirmed. ${L(this.adminResult.transactionUrl||"","View transaction")}${this.adminResult.bridged?"<p>Bridge delivery is tracked separately from the source transaction.</p>":""}${o("bridge","Check transfer status")}</div>`:""}`:""}title(){let e=this.snapshot;return this.wallets?"Connect your wallet":this.review?"Review payment":this.authorization?"Review authorization":this.pending?"Payment in progress":this.tab==="wallet"?e?.strategy==="rent"?"Deposit manager":"Wallet & rewards":this.tab==="community"||e&&!e.board?"Community treasury":this.form?.kind.startsWith("edit")?"Edit your Markee":this.form?this.form.entry.owner.toLowerCase()!==this.account?.toLowerCase()?"Fund a Markee":e?.strategy==="sale"?"Fund your Markee":BigInt(e?.wallet?.flowRate??"0")>0n?"Manage your Markee stream":"Stream to your Markee":"Change the Markee Sign"}footerHtml(){let e=this.snapshot,t="";if(this.wallets)t=o("back","Back");else if(this.review)t=o("back","Back")+o("send","Confirm in wallet","primary");else if(this.authorization)t=o("back","Back")+o("sign","Sign authorization","primary");else if(this.pending)t=o("check","Check transaction","primary");else if(e&&this.tab==="main"&&e.board){let a=this.form?.kind.startsWith("edit");t=(a?o("back","Cancel"):e.strategy==="rent"&&BigInt(e.wallet?.flowRate??"0")>0n?o("stop","Stop streaming","danger"):"")+(this.account?`<button class="btn primary" type="submit" form="markee-action">${a?"Review change":!this.form&&e.strategy==="rent"?"Review creation":"Review payment info"}</button>`:o("connect","Connect wallet","primary"))}else e&&(t=o("tab:main","Back to Markee"));return`<footer><div class="footer-actions">${t}</div><div class="footer-meta"><span>Powered by Markee${this.account?` \xB7 ${E(this.account)}`:""}</span>${e?.community&&e.board&&this.tab!=="community"&&!this.review&&!this.authorization&&!this.pending?o("tab:community","Community treasury","text-button"):""}</div></footer>`}render(){if(this.closed)return;let e=this.root.activeElement,t=e?.dataset.cmd,a=e?.id,i=this.wallets?"wallets":this.review?`review:${this.review.id}`:this.authorization?`authorization:${this.authorization.value.nonce}`:this.pending?`pending:${this.pending.hash}`:`${this.tab}:${this.form?.kind??"create"}:${this.form?.entry.address??""}`,r=i===this.renderedView?this.root.querySelector(".content")?.scrollTop??0:0;this.renderedView=i;let s=!document.activeElement||document.activeElement===document.body||document.activeElement===this.host,d=this.snapshot,c="";this.wallets?c=`<h2>Choose your wallet</h2><div class="wallets">${this.wallets.map((h,b)=>o(`wallet:${b}`,l(h.name))).join("")}</div>`:this.review?c=this.reviewHtml():this.authorization?c=this.authorizationHtml():d?this.pending?c=`<h2>Waiting for confirmation</h2><p>Your transaction was sent. You can close this modal and come back to check it.</p><p class="meta mono">${l(this.pending.hash)}</p><p class="note">Do not send a second payment while this transaction is pending.</p>`:!d.board||this.tab==="community"?c=this.communityHtml():this.tab==="wallet"?c=this.walletHtml():c=`${this.form?.kind.startsWith("edit")?"":this.entriesHtml()}${this.formHtml()}`:c=this.error?o("refresh","Retry","primary"):'<div class="loading"><span class="spinner"></span>Loading Markee\u2026</div>';let f=this.result?`<div class="${this.result.status==="reverted"||this.result.settlement==="deferred"?"error":"success"}">${this.result.status==="reverted"?"Transaction reverted. No action was completed.":this.result.settlement==="deferred"?"The transaction confirmed, but reward settlement was deferred. Your claim remains available.":`Transaction confirmed.${this.result.tokensReceived&&BigInt(this.result.tokensReceived)>0n?` ${m(this.result.tokensReceived,2)} MARKEE delivered to your wallet.`:""}`} ${L(this.result.explorerUrl,"View receipt")}</div>`:"";this.root.innerHTML=`<style>${H}</style><div class="overlay"><section class="panel" role="dialog" aria-modal="true" aria-labelledby="markee-title"><header><div class="grow"><h1 id="markee-title">${l(this.title())}</h1><p class="header-caption">${l(d?.name||"Markee")}${d?` \xB7 ${d.strategy==="rent"?"For rent":"For sale"}${d.chainId===8453?" \xB7 Base":d.chainId===11155111?" \xB7 Sepolia testnet":""}`:""}</p></div><button class="close" data-cmd="close" aria-label="Close Markee">\xD7</button></header><main class="content" aria-busy="${this.busy}"><div role="status" aria-live="polite">${this.busy?'<p class="meta"><span class="spinner"></span>Working\u2026</p>':""}${this.error?`<div class="error">${l(this.error)}</div>`:""}${f}</div>${d?.warnings.map(h=>`<div class="note">${l(h)}</div>`).join("")||""}${c}</main>${this.footerHtml()}</section></div>`,this.busy&&this.root.querySelectorAll('button:not([data-cmd="close"])').forEach(h=>{h.disabled=!0});let u=this.drafts.get(this.draftKey());if(u)for(let h of this.root.querySelectorAll("input,textarea,select"))u[h.name]!==void 0&&(h.value=u[h.name]);this.updateFields();let w=this.root.querySelector(".content");w&&(w.scrollTop=r),this.viewObserver?.disconnect();let I=this.root.querySelector(".winner"),y=d?.entries.find(h=>h.address.toLowerCase()===d.top?.toLowerCase()),M=y?`${y.address}:${y.message}`:"";I&&y?.moderated===!1&&!this.viewed.has(M)&&typeof IntersectionObserver<"u"&&(this.viewObserver=new IntersectionObserver(h=>{h.some(b=>b.isIntersecting)&&(this.viewObserver?.disconnect(),this.viewed.add(M),this.client.recordView(y.address,y.message,window.location.origin).catch(()=>{this.viewed.delete(M)}))},{threshold:.5}),this.viewObserver.observe(I)),s&&((a?this.root.getElementById(a):t?[...this.root.querySelectorAll("[data-cmd]")].find(b=>b.dataset.cmd===t):null)??this.root.querySelector('[data-cmd="close"]'))?.focus({preventScroll:!0})}};function B(n={}){let e=new T(n),t,a=n.theme;return{client:e,open(i){return t?.close(),t=new z(e,{...i,theme:i.theme??a}),t},setTheme(i){a=i??{},t?.setTheme(a)},close(){t?.close(),t=void 0}}}function re(n){let e=N(n);e.current=n;let t=N(),a=JSON.stringify(n.placement);return O(()=>{if(!n.open)return;let i=B({apiUrl:n.apiUrl,fetch:n.fetch,theme:e.current.theme,wallet:n.wallet?{getProvider:()=>e.current.wallet.getProvider(),connect:()=>{let s=e.current.wallet?.connect;return s?s():Promise.reject(new Error("Connect the app wallet first."))}}:void 0,onEvent:s=>e.current.onEvent?.(s)});t.current=i;let r=!1;return i.open({placement:n.placement,onClose:()=>{r||e.current.onClose()}}),()=>{r=!0,i.close(),t.current=void 0}},[n.open,n.apiUrl,n.fetch,!!n.wallet,a]),O(()=>{t.current?.setTheme(n.theme)},[n.theme]),null}function ne({children:n="Open Markee",onClick:e,...t}){return K("button",{...t,type:"button",onClick:e},n)}export{ne as MarkeeButton,re as MarkeeModal};
@@ -0,0 +1 @@
1
+ export declare const styles = "\n:host{all:initial;font-family:var(--markee-font-family);color:var(--markee-text);font-size:14px;line-height:1.5;color-scheme:var(--markee-color-scheme)}\n\n*{box-sizing:border-box}\nbutton,input,textarea,select{font:inherit}\nbutton{cursor:pointer}\nbutton:disabled{opacity:.5;cursor:wait}\nbutton:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,a:focus-visible{outline:3px solid var(--markee-focus-ring);outline-offset:3px}\n\n.overlay{position:fixed;inset:0;z-index:10000;background:var(--markee-overlay);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;padding:24px}\n\n.panel{background:var(--markee-background);border:1px solid var(--markee-border);border-radius:var(--markee-radius);width:min(512px,100%);max-height:92dvh;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 24px 100px #10183244;position:relative}\n\nheader{flex-shrink:0;padding:18px 24px;display:flex;gap:16px;align-items:flex-start;border-bottom:1px solid var(--markee-border)}\n.brand{width:40px;height:40px;display:grid;place-items:center;border-radius:var(--markee-control-radius);background:var(--markee-primary);color:var(--markee-primary-text);font-weight:900;font-size:24px}\n.grow{flex:1;min-width:0}\nh1{font-size:18px;line-height:1.25;margin:0 0 4px}\nh2{font-size:18px;line-height:1.3;margin:0 0 12px}\nh3{font-size:16px;margin:0 0 8px}\np{margin:8px 0 14px}\n.muted,small{color:var(--markee-muted)}\n.eyebrow{font-size:11px;text-transform:uppercase;letter-spacing:1.3px;font-weight:700;color:var(--markee-accent-surface-text)}\n.pill{display:inline-flex;border-radius:var(--markee-small-radius);background:var(--markee-accent-surface);color:var(--markee-accent-surface-text);padding:3px 7px;font-size:11px;font-weight:700}\n.close{border:0;background:none;color:var(--markee-muted);font-size:23px;padding:5px 10px;border-radius:var(--markee-small-radius)}\n.nav{display:flex;gap:4px;padding:12px 24px 0;overflow:auto}\n.nav button{white-space:nowrap;border:0;background:transparent;padding:10px 12px;color:var(--markee-muted);border-radius:var(--markee-control-radius);font-size:13px}\n.nav button[aria-selected=true]{background:var(--markee-selected);color:var(--markee-selected-text);font-weight:650}\n.content{padding:20px 24px;overflow-y:auto;overflow-x:hidden;min-height:0;overscroll-behavior:contain}\n.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}\n.between{justify-content:space-between}\n.stack{display:grid;gap:12px}\n.btn{border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);background:var(--markee-surface);color:var(--markee-text);padding:10px 15px;font-weight:600;font-size:13px}\n.primary{background:var(--markee-primary);border-color:var(--markee-primary);color:var(--markee-primary-text)}\n.accent{background:var(--markee-accent);border-color:var(--markee-accent);color:var(--markee-accent-text)}\n.danger{color:var(--markee-error-text)}\n.full{width:100%}\n.card{border:1px solid var(--markee-border);border-radius:var(--markee-card-radius);background:var(--markee-surface);padding:17px 18px}\n.winner{background:var(--markee-accent-surface);border-color:var(--markee-accent-border)}\n.message{font-family:ui-monospace,monospace;font-size:15px;overflow-wrap:anywhere;white-space:pre-wrap;line-height:1.5;margin:10px 0}\n.name{font-size:12px;color:var(--markee-muted)}\n.meta{font-size:12px;color:var(--markee-muted)}\n.stat{display:flex;justify-content:space-between;gap:16px;margin:8px 0;font-size:13px}\n.stat strong{text-align:right;overflow-wrap:anywhere}\n.grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}\n.metric{font-size:23px;font-weight:650;letter-spacing:-.6px}\n.note,.error,.success{padding:12px 14px;border-radius:var(--markee-control-radius);font-size:13px;overflow-wrap:anywhere;margin-bottom:14px}\n.note{background:var(--markee-info);color:var(--markee-info-text)}\n.error{background:var(--markee-error);color:var(--markee-error-text)}\n.success{background:var(--markee-success);color:var(--markee-success-text)}\nlabel{display:block;font-size:13px;font-weight:600;margin-bottom:6px}\ninput,textarea,select{display:block;width:100%;border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);background:var(--markee-input-background);color:var(--markee-input-text);padding:12px;min-width:0}\ninput::placeholder,textarea::placeholder{color:var(--markee-muted);opacity:1}\ntextarea{font-family:ui-monospace,monospace;font-size:14px;min-height:88px;max-height:220px;resize:vertical}\nform{display:grid;gap:16px}\n.field small{display:block;margin-top:5px;font-weight:400;font-size:12px}\n.divider{height:1px;background:var(--markee-border);margin:20px 0}\na{color:var(--markee-link);text-decoration:none;overflow-wrap:anywhere}\nul{padding-left:20px;font-size:14px}\n.review li{margin-bottom:9px;overflow-wrap:anywhere}\n.loading{padding:40px;text-align:center;color:var(--markee-muted)}\n.empty{padding:24px 8px;text-align:center}\n.spinner{display:inline-block;width:14px;height:14px;border-radius:100%;border:2px solid var(--markee-border);border-top-color:var(--markee-muted);animation:spin 1s linear infinite;margin-right:8px}\n@keyframes spin{to{transform:rotate(360deg)}\n}\nfooter{flex-shrink:0;padding:14px 20px;border-top:1px solid var(--markee-border);background:var(--markee-background);color:var(--markee-muted);font-size:11px}\n.mono{font-family:ui-monospace,monospace}\n.review{margin:16px 0}.review ul{padding-left:18px}.review .stat{border-top:1px solid var(--markee-border);padding-top:10px}.review-title{font-size:14px;color:var(--markee-muted);font-weight:500}\n.wallets{display:grid;gap:10px}\n\n\n.header-caption{font-size:12px;color:var(--markee-muted);margin:5px 0 0}\n.close{line-height:1;padding:4px 6px;flex-shrink:0}\n.text-button{border:0;background:transparent;color:var(--markee-link);padding:0;font-size:12px;font-weight:500;text-align:left}\n.compact{padding:5px 9px;font-size:11px;line-height:1.4}\n.leaderboard{border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);padding:12px 14px;background:var(--markee-surface);margin-bottom:20px}\n.leaderboard>.row{gap:8px}.leaderboard a{font-size:11px}.leaderboard .text-button{color:var(--markee-muted)}.chevron{display:inline-block;width:15px;font-size:16px}\n.leaderboard-list{display:grid;gap:10px;margin-top:12px;padding-top:12px;border-top:1px solid var(--markee-border)}\n.leaderboard-entry{display:flex;gap:12px;align-items:center;justify-content:space-between;border:1px solid var(--markee-border);border-radius:var(--markee-control-radius);padding:10px}\n.selected-entry{border-color:var(--markee-link)}.leaderboard-entry .message{font-size:12px;line-height:1.5;margin:0}.entry-caption{gap:5px;margin-top:5px}.entry-caption:empty{display:none}.entry-caption .pill{font-size:9px}.entry-caption .name,.entry-caption .meta{font-size:10px}\n.entry-funding{display:flex;flex-direction:column;align-items:flex-end;gap:4px;flex:0 0 auto;max-width:44%;text-align:right;overflow-wrap:anywhere}.entry-funding .meta{font-size:10px}\n.message-card{padding:16px;border:1px solid var(--markee-border);border-radius:var(--markee-card-radius);background:var(--markee-surface);min-width:0}\n.message-card.winner{background:var(--markee-accent-surface);border-color:var(--markee-accent-border)}\n.message-card>.message{font-size:17px;margin:12px 0 6px}.message-card .eyebrow{font-size:10px}.message-card>.name{margin:4px 0}\n.message-tools{margin-top:12px;gap:10px 16px}.message-tools .text-button{font-size:11px}.editor-card{display:grid;gap:16px}\n.field label{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.field-count{font-size:10px;font-weight:400;color:var(--markee-muted)}.field small{font-size:10px}\n.message-stats{border-top:1px solid var(--markee-border);margin-top:16px;padding-top:12px}.message-stats>.meta{font-size:11px}.stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:8px}.stats-grid>div{min-width:0}.stats-grid strong{display:block;font-family:ui-monospace,monospace;font-size:12px;overflow-wrap:anywhere}.stat-label{display:block;font-size:9px;letter-spacing:.6px;text-transform:uppercase;color:var(--markee-muted);margin-bottom:4px}\n.funding-card{border:1px solid var(--markee-link);border-radius:var(--markee-card-radius);background:var(--markee-surface);padding:16px;min-width:0}\n.funding-card>label{font-size:11px;color:var(--markee-muted);font-weight:500;margin-bottom:8px}.amount-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.amount-value{display:flex;gap:6px;align-items:baseline;min-width:0;flex:1 1 140px}.amount-value input{background:transparent;border:0;border-radius:2px;box-shadow:none;font-family:ui-monospace,monospace;font-size:23px;font-weight:600;min-width:0;width:100%;padding:3px 0;color:var(--markee-text)}.amount-value>span{font-size:11px;color:var(--markee-muted);white-space:nowrap}.amount-presets{display:flex;gap:6px;margin-left:auto}.amount-presets button{color:var(--markee-link)}.amount-presets button[aria-pressed=true]{border-color:var(--markee-link);background:var(--markee-selected);color:var(--markee-selected-text)}\n.funding-caption{display:flex;justify-content:space-between;flex-wrap:wrap;gap:4px 10px;margin-top:9px;color:var(--markee-muted);font-size:10px}.funding-tools{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;border-top:1px solid var(--markee-border);margin-top:13px;padding-top:12px}.funding-tools .meta{font-size:10px}.funding-hint{font-size:11px;margin:0}.funding-hint+.note{margin:0;font-size:11px}\n.footer-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.footer-actions>.primary:last-child{margin-left:auto}.footer-actions:empty{display:none}.footer-meta{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px;margin-top:10px;font-size:10px}.footer-meta .text-button{font-size:10px}\n\n@media(max-width:560px){.overlay{padding:8px;align-items:flex-end}\n.panel{border-radius:var(--markee-radius) var(--markee-radius) var(--markee-control-radius) var(--markee-control-radius);max-height:94dvh}\n.content{padding:20px 18px}\nheader{padding:20px 18px}\n.nav{padding-left:10px;padding-right:10px}\n.grid{grid-template-columns:1fr 1fr}\n.metric{font-size:19px}\nfooter{padding:12px 18px}\n.btn{padding:10px 12px}\n.compact{padding:5px 9px}.text-button{padding:0}.footer-actions>.btn{flex:1;white-space:normal}.footer-actions>.primary:last-child{margin-left:0}\n}\n\n@media(prefers-reduced-motion:reduce){*{animation:none!important;scroll-behavior:auto!important}\n}\n\n";
@@ -0,0 +1,6 @@
1
+ import type { MarkeeTheme } from './types.js';
2
+ /** Updates only scoped style properties; never replaces form or review DOM. */
3
+ export declare function createThemeController(host: HTMLElement, initial?: MarkeeTheme): {
4
+ set: (theme?: MarkeeTheme) => void;
5
+ destroy(): void;
6
+ };
@@ -0,0 +1,264 @@
1
+ export type Address = `0x${string}`;
2
+ export type Hex = `0x${string}`;
3
+ export type Placement = {
4
+ kind: 'board';
5
+ chainId: number;
6
+ address: Address;
7
+ } | {
8
+ kind: 'gardens-community';
9
+ chainId: number;
10
+ community: Address;
11
+ };
12
+ export interface Entry {
13
+ address: Address;
14
+ owner: Address;
15
+ message: string;
16
+ name: string;
17
+ score: string;
18
+ totalFunds: string;
19
+ aggregateRate: string;
20
+ legacyFloor: string;
21
+ moderated: boolean | null;
22
+ views: number | null;
23
+ }
24
+ export interface WalletState {
25
+ address: Address;
26
+ eth: string;
27
+ markee: string | null;
28
+ ethx: string;
29
+ backedMarkee: Address | null;
30
+ flowRate: string;
31
+ deposit: string;
32
+ withdrawableDeposit: string;
33
+ pendingSettlement: string;
34
+ refundRate: string;
35
+ pool: Address | null;
36
+ poolConnected: boolean;
37
+ poolClaimable: string;
38
+ netFlowRate: string;
39
+ runwaySeconds: string | null;
40
+ observedAt: number;
41
+ }
42
+ export interface CommunityState {
43
+ integration: {
44
+ status: 'not_integrated' | 'active';
45
+ leaderboardAddress: Address | null;
46
+ vaultAddress: Address | null;
47
+ };
48
+ markeeChainId: number;
49
+ revenue: {
50
+ claimableAmount: string;
51
+ symbol: string;
52
+ };
53
+ permissions?: {
54
+ canActivate: boolean;
55
+ canClaim: boolean;
56
+ councilSafe: Address | null;
57
+ };
58
+ }
59
+ export interface Snapshot {
60
+ freshness?: {
61
+ updatedAt: number;
62
+ freshUntil: number;
63
+ staleUntil: number;
64
+ stale: boolean;
65
+ refreshing: boolean;
66
+ };
67
+ apiVersion: 1;
68
+ placement: Placement;
69
+ board: Address | null;
70
+ chainId: number;
71
+ strategy: 'sale' | 'rent';
72
+ name: string;
73
+ minimum: string;
74
+ maxMessageBytes: number;
75
+ maxNameBytes: number;
76
+ revnetEnabled: boolean;
77
+ token: Address | null;
78
+ entries: Entry[];
79
+ nextOffset: number | null;
80
+ count: number;
81
+ top: Address | null;
82
+ rankedTop: Address | null;
83
+ wallet: WalletState | null;
84
+ community?: CommunityState;
85
+ warnings: string[];
86
+ blockNumber: string;
87
+ metadata?: {
88
+ siteUrl: string | null;
89
+ logoUrl: string | null;
90
+ verifiedUrls: string[];
91
+ } | null;
92
+ }
93
+ export type Action = {
94
+ kind: 'create';
95
+ message: string;
96
+ name: string;
97
+ amount?: string;
98
+ } | {
99
+ kind: 'fund';
100
+ markee: Address;
101
+ amount: string;
102
+ } | {
103
+ kind: 'edit-message';
104
+ markee: Address;
105
+ message: string;
106
+ } | {
107
+ kind: 'edit-name';
108
+ markee: Address;
109
+ name: string;
110
+ } | {
111
+ kind: 'stream';
112
+ markee: Address;
113
+ monthlyAmount: string;
114
+ } | {
115
+ kind: 'stop' | 'withdraw-buffer' | 'settle';
116
+ } | {
117
+ kind: 'claim-refund' | 'connect-refund';
118
+ markee: Address;
119
+ } | {
120
+ kind: 'wrap' | 'unwrap';
121
+ amount: string;
122
+ } | {
123
+ kind: 'claim-top';
124
+ markee: Address;
125
+ };
126
+ export interface PreparedAction {
127
+ id: string;
128
+ expiresAt: number;
129
+ placement: Placement;
130
+ action: Action;
131
+ transaction: {
132
+ from: Address;
133
+ to: Address;
134
+ chainId: number;
135
+ data: Hex;
136
+ value: Hex;
137
+ gas: Hex;
138
+ };
139
+ review: {
140
+ title: string;
141
+ lines: string[];
142
+ estimatedGasCost: string;
143
+ reward: string;
144
+ };
145
+ }
146
+ export interface TransactionResult {
147
+ status: 'pending' | 'confirmed' | 'reverted';
148
+ hash: Hex;
149
+ explorerUrl: string;
150
+ createdMarkee?: Address;
151
+ tokensReceived?: string;
152
+ settlement?: 'paid' | 'deferred';
153
+ }
154
+ export interface ClaimQuote {
155
+ bridgeProtocol: 'across' | 'lifi' | 'squid' | 'none';
156
+ bridged: boolean;
157
+ claimAmount: string;
158
+ estimatedFeeAmount: string;
159
+ estimatedNetworkFeeAmount: string;
160
+ expectedAmountOut: string;
161
+ destinationSymbol: string;
162
+ expiresAt: number;
163
+ markeeChainId: number;
164
+ }
165
+ export interface Authorization {
166
+ nonce: string;
167
+ typedData: {
168
+ domain: {
169
+ name: string;
170
+ version: string;
171
+ chainId: number;
172
+ verifyingContract: Address;
173
+ };
174
+ types: Record<string, {
175
+ name: string;
176
+ type: string;
177
+ }[]>;
178
+ primaryType: string;
179
+ message: Record<string, unknown>;
180
+ };
181
+ }
182
+ export interface AdminResult {
183
+ transactionHash: Hex;
184
+ transactionUrl?: string;
185
+ bridgeName?: string | null;
186
+ bridged?: boolean;
187
+ bridgeProtocol?: ClaimQuote['bridgeProtocol'];
188
+ expectedAmountOut?: string;
189
+ destinationSymbol?: string;
190
+ }
191
+ export interface AdminOperation {
192
+ id: string;
193
+ status: 'working' | 'submitted' | 'complete' | 'failed';
194
+ result?: AdminResult;
195
+ error?: string;
196
+ }
197
+ export interface BridgeStatus {
198
+ status: 'needs_gas' | 'not_found' | 'ongoing' | 'partial_success' | 'refund' | 'success' | 'unknown';
199
+ destinationTransactionUrl: string | null;
200
+ sourceTransactionUrl: string | null;
201
+ }
202
+ export interface EIP1193Provider {
203
+ request(args: {
204
+ method: string;
205
+ params?: readonly unknown[] | object;
206
+ }): Promise<unknown>;
207
+ on?(event: string, listener: (...args: unknown[]) => void): void;
208
+ removeListener?(event: string, listener: (...args: unknown[]) => void): void;
209
+ }
210
+ /** Return the app's selected wallet. Never create a second wallet session. */
211
+ export interface WalletAdapter {
212
+ getProvider(): Promise<EIP1193Provider | null>;
213
+ connect?(): Promise<EIP1193Provider>;
214
+ }
215
+ /** Semantic color pairs used throughout the modal, including forms and notices. */
216
+ export interface MarkeeThemeColors {
217
+ background: string;
218
+ surface: string;
219
+ text: string;
220
+ muted: string;
221
+ border: string;
222
+ inputBackground: string;
223
+ inputText: string;
224
+ primary: string;
225
+ primaryText: string;
226
+ accent: string;
227
+ accentText: string;
228
+ accentSurface: string;
229
+ accentSurfaceText: string;
230
+ accentBorder: string;
231
+ selected: string;
232
+ selectedText: string;
233
+ info: string;
234
+ infoText: string;
235
+ error: string;
236
+ errorText: string;
237
+ success: string;
238
+ successText: string;
239
+ link: string;
240
+ focusRing: string;
241
+ overlay: string;
242
+ }
243
+ export interface MarkeeTheme {
244
+ /** Defaults to light. System follows the OS preference, including live changes. */
245
+ mode?: 'light' | 'dark' | 'system';
246
+ /** Overrides the selected palette. Set foreground/background pairs together. */
247
+ colors?: Partial<MarkeeThemeColors>;
248
+ /** A CSS font-family stack. Fonts are supplied by the host app. */
249
+ fontFamily?: string;
250
+ /** Panel corner radius in pixels, clamped to 0–48. Controls scale with it. Default: 22. */
251
+ radius?: number;
252
+ }
253
+ export interface SDKOptions {
254
+ /** Optional development override; defaults to the Markee-hosted API. */
255
+ apiUrl?: string;
256
+ wallet?: WalletAdapter;
257
+ fetch?: typeof fetch;
258
+ theme?: MarkeeTheme;
259
+ onEvent?: (event: {
260
+ type: 'submitted' | 'confirmed' | 'error';
261
+ hash?: Hex;
262
+ message?: string;
263
+ }) => void;
264
+ }
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "@markee_team/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0-alpha.2",
4
+ "description": "Self-contained Markee modal and API client, with an optional React adapter.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./react": {
18
+ "types": "./dist/react.d.ts",
19
+ "import": "./dist/react.js"
20
+ }
21
+ },
22
+ "scripts": {
23
+ "build": "tsc && esbuild src/index.ts src/react.ts --bundle --format=esm --outdir=dist --external:react --minify",
24
+ "typecheck": "tsc --noEmit",
25
+ "test": "tsx --test tests/*.test.ts",
26
+ "prepack": "npm run build",
27
+ "test:declarations": "tsc --noEmit --strict --target ES2022 --module NodeNext --moduleResolution NodeNext dist/index.d.ts dist/react.d.ts"
28
+ },
29
+ "peerDependencies": {
30
+ "react": "^18.2.0 || ^19.0.0"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "react": {
34
+ "optional": true
35
+ }
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^20.0.0",
39
+ "@types/react": "^18.3.0",
40
+ "esbuild": "^0.25.0",
41
+ "tsx": "^4.22.4",
42
+ "typescript": "^5.9.3"
43
+ },
44
+ "engines": {
45
+ "node": ">=20"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/1Hive/markee.git",
50
+ "directory": "packages/sdk"
51
+ },
52
+ "homepage": "https://github.com/1Hive/markee/tree/feature/markee-sdk/packages/sdk#readme",
53
+ "bugs": {
54
+ "url": "https://github.com/1Hive/markee/issues"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public",
58
+ "tag": "alpha",
59
+ "registry": "https://registry.npmjs.org/"
60
+ },
61
+ "main": "./dist/index.js",
62
+ "types": "./dist/index.d.ts",
63
+ "license": "MIT"
64
+ }