create-partylayer-app 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +10 -0
- package/dist/templates.js.map +1 -1
- package/package.json +1 -1
- package/templates/next-ssr/README.md +4 -4
- package/templates/next-ssr/_package.json +1 -1
- package/templates/react-dvp/README.md +134 -0
- package/templates/react-dvp/_gitignore +8 -0
- package/templates/react-dvp/_package.json +26 -0
- package/templates/react-dvp/index.html +12 -0
- package/templates/react-dvp/src/App.css +385 -0
- package/templates/react-dvp/src/App.tsx +110 -0
- package/templates/react-dvp/src/context/DemoContext.tsx +34 -0
- package/templates/react-dvp/src/index.css +23 -0
- package/templates/react-dvp/src/lib/backend.ts +144 -0
- package/templates/react-dvp/src/lib/fixtures.ts +113 -0
- package/templates/react-dvp/src/lib/format.ts +59 -0
- package/templates/react-dvp/src/lib/invalidate.ts +18 -0
- package/templates/react-dvp/src/lib/match.ts +28 -0
- package/templates/react-dvp/src/lib/mutation.ts +17 -0
- package/templates/react-dvp/src/lib/store.ts +332 -0
- package/templates/react-dvp/src/lib/types.ts +35 -0
- package/templates/react-dvp/src/main.tsx +18 -0
- package/templates/react-dvp/src/sections/Holdings.tsx +51 -0
- package/templates/react-dvp/src/sections/MyAllocations.tsx +79 -0
- package/templates/react-dvp/src/sections/Trades.tsx +298 -0
- package/templates/react-dvp/src/ui/primitives.tsx +91 -0
- package/templates/react-dvp/tsconfig.json +20 -0
- package/templates/react-dvp/vite.config.ts +7 -0
- package/templates/react-tokenization/README.md +139 -0
- package/templates/react-tokenization/_gitignore +8 -0
- package/templates/react-tokenization/_package.json +26 -0
- package/templates/react-tokenization/index.html +12 -0
- package/templates/react-tokenization/src/App.css +361 -0
- package/templates/react-tokenization/src/App.tsx +111 -0
- package/templates/react-tokenization/src/context/DemoContext.tsx +34 -0
- package/templates/react-tokenization/src/index.css +23 -0
- package/templates/react-tokenization/src/lib/backend.ts +171 -0
- package/templates/react-tokenization/src/lib/fixtures.ts +134 -0
- package/templates/react-tokenization/src/lib/format.ts +59 -0
- package/templates/react-tokenization/src/lib/invalidate.ts +23 -0
- package/templates/react-tokenization/src/lib/mutation.ts +17 -0
- package/templates/react-tokenization/src/lib/store.ts +219 -0
- package/templates/react-tokenization/src/lib/types.ts +27 -0
- package/templates/react-tokenization/src/main.tsx +18 -0
- package/templates/react-tokenization/src/sections/Allocations.tsx +150 -0
- package/templates/react-tokenization/src/sections/Holdings.tsx +51 -0
- package/templates/react-tokenization/src/sections/Incoming.tsx +107 -0
- package/templates/react-tokenization/src/sections/Issuer.tsx +177 -0
- package/templates/react-tokenization/src/sections/Transfer.tsx +112 -0
- package/templates/react-tokenization/src/ui/primitives.tsx +91 -0
- package/templates/react-tokenization/tsconfig.json +20 -0
- package/templates/react-tokenization/vite.config.ts +7 -0
- package/templates/react-vite/README.md +2 -2
- package/templates/react-vite/_package.json +1 -1
- package/templates/vanilla/README.md +2 -2
- package/templates/vanilla/_package.json +1 -1
- package/templates/vanilla/src/main.ts +2 -2
- package/templates/vue-nuxt-pinia/README.md +3 -3
- package/templates/vue-nuxt-pinia/_package.json +1 -1
package/dist/templates.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,SAAS,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,SAAS,EAAE,YAAY,EA+BnC,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD"}
|
package/dist/templates.js
CHANGED
|
@@ -4,6 +4,16 @@ export const TEMPLATES = [
|
|
|
4
4
|
label: 'React + Vite',
|
|
5
5
|
hint: 'React 18, Vite, zero-config PartyLayerKit',
|
|
6
6
|
},
|
|
7
|
+
{
|
|
8
|
+
id: 'react-tokenization',
|
|
9
|
+
label: 'React + Tokenization vertical',
|
|
10
|
+
hint: 'CIP-0056 holdings, transfers, and issuance over a typed demo backend',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'react-dvp',
|
|
14
|
+
label: 'React + DvP vertical',
|
|
15
|
+
hint: 'Atomic delivery versus payment over CIP-0056 allocations',
|
|
16
|
+
},
|
|
7
17
|
{
|
|
8
18
|
id: 'next-ssr',
|
|
9
19
|
label: 'Next.js (SSR)',
|
package/dist/templates.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,SAAS,GAAmB;IACvC;QACE,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,2CAA2C;KAClD;IACD;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE,mDAAmD;KAC1D;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,yDAAyD;KAChE;IACD;QACE,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,sDAAsD;KAC7D;CACF,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;AAC/C,CAAC"}
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,SAAS,GAAmB;IACvC;QACE,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,2CAA2C;KAClD;IACD;QACE,EAAE,EAAE,oBAAoB;QACxB,KAAK,EAAE,+BAA+B;QACtC,IAAI,EAAE,sEAAsE;KAC7E;IACD;QACE,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,sBAAsB;QAC7B,IAAI,EAAE,0DAA0D;KACjE;IACD;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE,mDAAmD;KAC1D;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,yDAAyD;KAChE;IACD;QACE,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,sDAAsD;KAC7D;CACF,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;AAC/C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
|
-
A [PartyLayer](https://partylayer.xyz) dApp
|
|
3
|
+
A [PartyLayer](https://partylayer.xyz) dApp: Next.js App Router with **SSR session**.
|
|
4
4
|
|
|
5
5
|
## Develop
|
|
6
6
|
|
|
@@ -11,8 +11,8 @@ npm run dev
|
|
|
11
11
|
## How SSR session works
|
|
12
12
|
|
|
13
13
|
- **Server** ([lib/session.ts](lib/session.ts)) reads the session cookie via `next/headers` `cookies()` + `@partylayer/session`'s `createCookieStorage` → the connected party is rendered in the **initial HTML** (no disconnected→connected flash). `app/page.tsx` is a Server Component that gates on `getServerSession()`.
|
|
14
|
-
- **Client** ([app/providers.tsx](app/providers.tsx)) wraps the app in `PartyLayerKit` with `sessionOptions={{ storage: createCookieStorage() }}
|
|
15
|
-
- `next/headers` is imported only in your app (`lib/session.ts`), never in `@partylayer/session
|
|
14
|
+
- **Client** ([app/providers.tsx](app/providers.tsx)) wraps the app in `PartyLayerKit` with `sessionOptions={{ storage: createCookieStorage() }}`. This uses `document.cookie` (synchronous), the **same** cookie the server reads.
|
|
15
|
+
- `next/headers` is imported only in your app (`lib/session.ts`), never in `@partylayer/session`. The library stays framework-agnostic.
|
|
16
16
|
|
|
17
17
|
Switch networks by changing `network` in `app/providers.tsx` and `appName` accordingly.
|
|
18
18
|
|
|
@@ -25,4 +25,4 @@ npm run build && npm start
|
|
|
25
25
|
## Docs
|
|
26
26
|
|
|
27
27
|
- [PartyLayer docs](https://partylayer.xyz/docs/introduction)
|
|
28
|
-
- [@partylayer/session
|
|
28
|
+
- [@partylayer/session: createCookieStorage](https://www.npmjs.com/package/@partylayer/session)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
A DvP (delivery versus payment) vertical for Canton, scaffolded with
|
|
4
|
+
create-partylayer-app. A single-page dApp where a settlement venue runs an atomic
|
|
5
|
+
two-leg trade over the CIP-0056 (Canton Token Standard) allocation surface. A trade
|
|
6
|
+
IS an allocation request; each counterparty allocates its leg; the venue settles both
|
|
7
|
+
legs in one all-or-nothing step.
|
|
8
|
+
|
|
9
|
+
Everything runs on an in-memory demo backend (typed fixtures behind a small
|
|
10
|
+
`DvpBackend` interface), so every hook is exercised for real (loading, success,
|
|
11
|
+
error, invalidation) without a live ledger. Model 2 throughout: PartyLayer owns none
|
|
12
|
+
of the transport; the dApp supplies every read and submit fetcher, and the hooks type
|
|
13
|
+
the request and wrap the query or mutation.
|
|
14
|
+
|
|
15
|
+
> Reference quality, NOT production audited. This template demonstrates the API in a
|
|
16
|
+
> realistic composition. Review, test, and harden it before using it in production.
|
|
17
|
+
|
|
18
|
+
## Run it
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm create partylayer-app@latest my-app -- --template react-dvp
|
|
22
|
+
cd my-app
|
|
23
|
+
npm install
|
|
24
|
+
npm run dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## What is on screen
|
|
28
|
+
|
|
29
|
+
A header with the connect UI, a demo-party switcher (venue / alice / bob), a
|
|
30
|
+
synchronizer picker, and a light/dark toggle (the gold theme family); then three
|
|
31
|
+
section cards:
|
|
32
|
+
|
|
33
|
+
| Section | Hook(s) exercised |
|
|
34
|
+
| :-- | :-- |
|
|
35
|
+
| Trades | `useAllocationRequests`, `useAllocationInstruction`, `useAllocationRequestAction` (counterparty); `useTokenAllocations`, `useChoice`, `useAllocationAction` (venue) |
|
|
36
|
+
| My allocations | `useTokenAllocations` and `useAllocationAction` (withdraw) |
|
|
37
|
+
| Holdings | `useTokenHoldings` |
|
|
38
|
+
|
|
39
|
+
A trade is an allocation request: `settlement { executor: the venue, settlementRef,
|
|
40
|
+
requestedAt, allocateBefore, settleBefore }` plus two opposite `transferLegs` (Alice
|
|
41
|
+
pays DEMO-USD to Bob; Bob delivers DEMO-BOND to Alice). A non-selectable `registry`
|
|
42
|
+
party administers both instruments; the venue (executor) and the registry are
|
|
43
|
+
distinct roles.
|
|
44
|
+
|
|
45
|
+
## Demo flows
|
|
46
|
+
|
|
47
|
+
1. Connect and create a trade. As the Venue, click Connect Wallet (the console
|
|
48
|
+
adapter, themed by the gold family). Enter a USD amount and a BOND amount and click
|
|
49
|
+
New trade: a new allocation request appears.
|
|
50
|
+
2. Alice allocates her USD leg. Switch to Alice. Her leg shows a `CostPreview` before
|
|
51
|
+
confirm; Allocate my leg composes the allocation spec from the request and submits
|
|
52
|
+
it through `useAllocationInstruction`. Her USD holdings drop and a change holding is
|
|
53
|
+
written back.
|
|
54
|
+
3. Bob allocates his BOND leg. Switch to Bob and allocate his leg the same way.
|
|
55
|
+
4. The venue settles atomically. Switch to the Venue. The trade shows 2/2 allocated
|
|
56
|
+
and Settle is enabled. Settle moves both legs in one step: Bob receives the USD,
|
|
57
|
+
Alice receives the BOND, and the balances swap.
|
|
58
|
+
5. Withdraw then settle shows the failure. Allocate a leg, then withdraw it from My
|
|
59
|
+
allocations. The venue's Settle is disabled while a leg is unmatched; the store
|
|
60
|
+
itself throws a descriptive all-or-nothing error (listing the missing leg ids) that
|
|
61
|
+
moves nothing.
|
|
62
|
+
6. Bob rejects a trade. Switch to Bob and Reject an open trade through
|
|
63
|
+
`useAllocationRequestAction` (action `reject`, actor = Bob). Any allocations for it
|
|
64
|
+
are released and the trade is dropped.
|
|
65
|
+
7. The venue withdraws a trade. As the Venue, Withdraw trade through
|
|
66
|
+
`useAllocationRequestAction` (action `withdraw`), releasing allocations and dropping
|
|
67
|
+
the trade.
|
|
68
|
+
|
|
69
|
+
The venue can also Cancel allocation on a matched leg (`useAllocationAction`, action
|
|
70
|
+
`cancel`) for the abort path that releases a counterparty's backing early.
|
|
71
|
+
|
|
72
|
+
## Party-scoped caching
|
|
73
|
+
|
|
74
|
+
Every hook folds the current demo party into its `key` (for example
|
|
75
|
+
`['dvp', 'holdings', party]`), so each party's data is cached independently. Switching
|
|
76
|
+
parties never shows another party's cached data. Invalidation after every write goes
|
|
77
|
+
through the exported `partyLayerKeys` factories (see `src/lib/invalidate.ts`),
|
|
78
|
+
refreshing the trades (`allocationRequests`), allocations (`tokenAllocations`), and
|
|
79
|
+
holdings (`tokenHoldings`).
|
|
80
|
+
|
|
81
|
+
## Atomic settle
|
|
82
|
+
|
|
83
|
+
The venue's settle mirrors the official trading-app `OTCTrade_Settle`: it verifies an
|
|
84
|
+
expected allocation exists for EVERY leg (a spec match on settlement, transfer leg id,
|
|
85
|
+
and transfer leg) and that `now < settleBefore`, then executes ALL legs in ONE store
|
|
86
|
+
mutation. If any leg is unallocated or the deadline has passed it throws BEFORE any
|
|
87
|
+
asset moves, listing the missing leg ids. This is a dApp-specific trade choice, so the
|
|
88
|
+
UI wires it through the generic `useChoice`: a real venue settles atomically via its
|
|
89
|
+
trade contract's choice whose body exercises `Allocation_ExecuteTransfer` per leg in a
|
|
90
|
+
single transaction, which is exactly why this is `useChoice` and not the
|
|
91
|
+
per-allocation typed hook.
|
|
92
|
+
|
|
93
|
+
The comparator that decides whether an allocation satisfies a request leg is
|
|
94
|
+
`allocationMatchesRequestLeg` from `@partylayer/react/query` (decimal-as-string
|
|
95
|
+
equality without float conversion), used by both the store and the venue view via the
|
|
96
|
+
thin adapter in `src/lib/match.ts`.
|
|
97
|
+
|
|
98
|
+
## Decimal handling
|
|
99
|
+
|
|
100
|
+
Amounts are decimal strings end to end (never JS numbers), matching the CIP-0056
|
|
101
|
+
`Decimal` convention. The demo store simplifies arithmetic to two decimal places using
|
|
102
|
+
bigint cents (`src/lib/format.ts`); a real dApp handling arbitrary precision should use
|
|
103
|
+
a decimal library.
|
|
104
|
+
|
|
105
|
+
## Real mode
|
|
106
|
+
|
|
107
|
+
To wire this against a live validator and registry, replace the demo backend's
|
|
108
|
+
fetchers. The hooks and the UI stay exactly as they are.
|
|
109
|
+
|
|
110
|
+
- Discovery. Read the trades a party can act on with an ACS interface-filter query for
|
|
111
|
+
`Splice.Api.Token.AllocationRequestV1:AllocationRequest`, mapped into
|
|
112
|
+
`TokenAllocationRequestRef`. Implementations SHOULD make at least all transfer-leg
|
|
113
|
+
senders observers of the request, which is how wallets discover it.
|
|
114
|
+
- Allocate a leg (`useAllocationInstruction`). Compose the allocation spec from the
|
|
115
|
+
request (`settlement`, `transferLegId`, `transferLeg`), then in the `submit` fetcher
|
|
116
|
+
fetch the registry's allocation-factory (its `choiceContext` and
|
|
117
|
+
`disclosedContracts`), fill `extraArgs.context`, and exercise
|
|
118
|
+
`AllocationFactory_Allocate` with the disclosed contracts. `expectedAdmin` must come
|
|
119
|
+
from a trusted source (e.g. a read against your own participant).
|
|
120
|
+
- Respond to a request (`useAllocationRequestAction`). `AllocationRequest_Reject` (with
|
|
121
|
+
`actor`) and `AllocationRequest_Withdraw` are called with an EMPTY choice context per
|
|
122
|
+
the standard, so the `submit` fetcher exercises directly on the request cid with
|
|
123
|
+
`extraArgs { context: empty, meta }`, no registry fetch.
|
|
124
|
+
- Act on a funded allocation (`useAllocationAction`). Withdraw and cancel go through
|
|
125
|
+
the registry's per-action choice contexts on the allocation cid.
|
|
126
|
+
- Settle. The venue's trade app exercises its own settle choice, whose body runs
|
|
127
|
+
`Allocation_ExecuteTransfer` for every leg in one transaction. The official Splice
|
|
128
|
+
token-standard trading-app example is the canonical reference for this flow.
|
|
129
|
+
|
|
130
|
+
For the shapes these fetchers pass around, `@partylayer/react/query` exports the
|
|
131
|
+
disclosed contract and choice context types (`TokenDisclosedContract`,
|
|
132
|
+
`TokenChoiceContext`, `TokenTransferFactory`, `TokenAllocationFactory`) plus
|
|
133
|
+
`mergeDisclosedContracts` for combining the disclosures of several registry contexts
|
|
134
|
+
into one submission.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@partylayer/react": "^2.2.1",
|
|
13
|
+
"@partylayer/sdk": "^0.16.0",
|
|
14
|
+
"@partylayer/adapter-console": "^0.3.15",
|
|
15
|
+
"@tanstack/react-query": "^5.0.0",
|
|
16
|
+
"react": "^18.3.1",
|
|
17
|
+
"react-dom": "^18.3.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/react": "^18.3.3",
|
|
21
|
+
"@types/react-dom": "^18.3.0",
|
|
22
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
23
|
+
"typescript": "^5.6.2",
|
|
24
|
+
"vite": "^5.4.2"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>{{PROJECT_NAME}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/* App chrome. Two token sets follow the light/dark toggle, tuned to the gold
|
|
2
|
+
family so the chrome matches the PartyLayer primitives (which retint from the
|
|
3
|
+
gold theme independently). This is what differentiates DvP from tokenization's teal. */
|
|
4
|
+
.app {
|
|
5
|
+
--bg: #ffffff;
|
|
6
|
+
--surface: #fefce8;
|
|
7
|
+
--card: #ffffff;
|
|
8
|
+
--text: #1c1917;
|
|
9
|
+
--muted: #78716c;
|
|
10
|
+
--border: rgba(28, 25, 23, 0.14);
|
|
11
|
+
--accent: #b45309;
|
|
12
|
+
--accent-contrast: #ffffff;
|
|
13
|
+
--error-bg: #fef2f2;
|
|
14
|
+
--error-text: #b00020;
|
|
15
|
+
--lock-bg: #fff7ed;
|
|
16
|
+
--lock-text: #9a3412;
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
background: var(--bg);
|
|
19
|
+
color: var(--text);
|
|
20
|
+
transition: background 200ms, color 200ms;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.app-dark {
|
|
24
|
+
--bg: #0c0a09;
|
|
25
|
+
--surface: #1c1917;
|
|
26
|
+
--card: #1c1917;
|
|
27
|
+
--text: #fafaf9;
|
|
28
|
+
--muted: #a8a29e;
|
|
29
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
30
|
+
--accent: #fbbf24;
|
|
31
|
+
--accent-contrast: #1c1917;
|
|
32
|
+
--error-bg: #2a0f10;
|
|
33
|
+
--error-text: #fca5a5;
|
|
34
|
+
--lock-bg: #2a1c0f;
|
|
35
|
+
--lock-text: #fbbf24;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.topbar {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: space-between;
|
|
42
|
+
gap: 16px;
|
|
43
|
+
flex-wrap: wrap;
|
|
44
|
+
padding: 16px 24px;
|
|
45
|
+
border-bottom: 1px solid var(--border);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.brand {
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: 10px;
|
|
52
|
+
}
|
|
53
|
+
.brand-mark {
|
|
54
|
+
width: 28px;
|
|
55
|
+
height: 28px;
|
|
56
|
+
border-radius: 8px;
|
|
57
|
+
background: var(--accent);
|
|
58
|
+
display: inline-block;
|
|
59
|
+
}
|
|
60
|
+
.brand-title {
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
letter-spacing: -0.01em;
|
|
63
|
+
}
|
|
64
|
+
.brand-sub {
|
|
65
|
+
font-size: 12px;
|
|
66
|
+
color: var(--muted);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.topbar-controls {
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: 12px;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.party-switch {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
gap: 6px;
|
|
80
|
+
padding: 4px 8px;
|
|
81
|
+
border: 1px solid var(--border);
|
|
82
|
+
border-radius: 10px;
|
|
83
|
+
}
|
|
84
|
+
.party-switch-label {
|
|
85
|
+
font-size: 11px;
|
|
86
|
+
text-transform: uppercase;
|
|
87
|
+
letter-spacing: 0.06em;
|
|
88
|
+
color: var(--muted);
|
|
89
|
+
margin-right: 2px;
|
|
90
|
+
}
|
|
91
|
+
.party-chip {
|
|
92
|
+
border: none;
|
|
93
|
+
background: transparent;
|
|
94
|
+
color: var(--muted);
|
|
95
|
+
padding: 4px 10px;
|
|
96
|
+
border-radius: 8px;
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
font-size: 13px;
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
}
|
|
101
|
+
.party-chip-on {
|
|
102
|
+
background: var(--accent);
|
|
103
|
+
color: var(--accent-contrast);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.acting-line {
|
|
107
|
+
max-width: 1100px;
|
|
108
|
+
margin: 16px auto 0;
|
|
109
|
+
padding: 0 24px;
|
|
110
|
+
color: var(--muted);
|
|
111
|
+
font-size: 14px;
|
|
112
|
+
}
|
|
113
|
+
.acting-line code {
|
|
114
|
+
font-size: 12px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.grid {
|
|
118
|
+
max-width: 1100px;
|
|
119
|
+
margin: 16px auto 0;
|
|
120
|
+
padding: 0 24px 40px;
|
|
121
|
+
display: grid;
|
|
122
|
+
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
123
|
+
gap: 20px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.card {
|
|
127
|
+
background: var(--card);
|
|
128
|
+
border: 1px solid var(--border);
|
|
129
|
+
border-radius: 14px;
|
|
130
|
+
overflow: hidden;
|
|
131
|
+
}
|
|
132
|
+
.card-head {
|
|
133
|
+
display: flex;
|
|
134
|
+
align-items: baseline;
|
|
135
|
+
justify-content: space-between;
|
|
136
|
+
gap: 10px;
|
|
137
|
+
padding: 14px 16px;
|
|
138
|
+
border-bottom: 1px solid var(--border);
|
|
139
|
+
}
|
|
140
|
+
.card-head h2 {
|
|
141
|
+
margin: 0;
|
|
142
|
+
font-size: 16px;
|
|
143
|
+
}
|
|
144
|
+
.card-hint {
|
|
145
|
+
font-size: 11px;
|
|
146
|
+
color: var(--muted);
|
|
147
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
148
|
+
}
|
|
149
|
+
.card-body {
|
|
150
|
+
padding: 16px;
|
|
151
|
+
display: flex;
|
|
152
|
+
flex-direction: column;
|
|
153
|
+
gap: 14px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.list {
|
|
157
|
+
list-style: none;
|
|
158
|
+
margin: 0;
|
|
159
|
+
padding: 0;
|
|
160
|
+
display: flex;
|
|
161
|
+
flex-direction: column;
|
|
162
|
+
gap: 10px;
|
|
163
|
+
}
|
|
164
|
+
.row {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
gap: 12px;
|
|
168
|
+
padding: 10px 12px;
|
|
169
|
+
border: 1px solid var(--border);
|
|
170
|
+
border-radius: 10px;
|
|
171
|
+
}
|
|
172
|
+
.row-block {
|
|
173
|
+
flex-direction: column;
|
|
174
|
+
align-items: stretch;
|
|
175
|
+
gap: 10px;
|
|
176
|
+
}
|
|
177
|
+
.row-block .row-actions {
|
|
178
|
+
justify-content: flex-start;
|
|
179
|
+
}
|
|
180
|
+
.row-main {
|
|
181
|
+
flex: 1;
|
|
182
|
+
min-width: 0;
|
|
183
|
+
}
|
|
184
|
+
.row-title {
|
|
185
|
+
font-weight: 600;
|
|
186
|
+
}
|
|
187
|
+
.row-sub {
|
|
188
|
+
font-size: 12px;
|
|
189
|
+
word-break: break-all;
|
|
190
|
+
}
|
|
191
|
+
.row-actions {
|
|
192
|
+
display: flex;
|
|
193
|
+
gap: 8px;
|
|
194
|
+
flex-wrap: wrap;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.muted {
|
|
198
|
+
color: var(--muted);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.badge {
|
|
202
|
+
font-size: 11px;
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
padding: 3px 8px;
|
|
205
|
+
border-radius: 999px;
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
}
|
|
208
|
+
.badge-neutral {
|
|
209
|
+
background: var(--surface);
|
|
210
|
+
color: var(--muted);
|
|
211
|
+
}
|
|
212
|
+
.badge-lock {
|
|
213
|
+
background: var(--lock-bg);
|
|
214
|
+
color: var(--lock-text);
|
|
215
|
+
}
|
|
216
|
+
.badge-ok {
|
|
217
|
+
background: var(--surface);
|
|
218
|
+
color: var(--accent);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.form-grid {
|
|
222
|
+
display: grid;
|
|
223
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
224
|
+
gap: 12px;
|
|
225
|
+
align-items: end;
|
|
226
|
+
}
|
|
227
|
+
.field-action .btn {
|
|
228
|
+
width: 100%;
|
|
229
|
+
}
|
|
230
|
+
.field {
|
|
231
|
+
display: flex;
|
|
232
|
+
flex-direction: column;
|
|
233
|
+
gap: 4px;
|
|
234
|
+
}
|
|
235
|
+
.field-label {
|
|
236
|
+
font-size: 12px;
|
|
237
|
+
color: var(--muted);
|
|
238
|
+
}
|
|
239
|
+
.field-action {
|
|
240
|
+
justify-content: flex-end;
|
|
241
|
+
}
|
|
242
|
+
.field input,
|
|
243
|
+
.field select,
|
|
244
|
+
.topbar-controls select {
|
|
245
|
+
padding: 8px 10px;
|
|
246
|
+
border: 1px solid var(--border);
|
|
247
|
+
border-radius: 8px;
|
|
248
|
+
background: var(--bg);
|
|
249
|
+
color: var(--text);
|
|
250
|
+
font-size: 14px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.btn {
|
|
254
|
+
border: 1px solid transparent;
|
|
255
|
+
border-radius: 8px;
|
|
256
|
+
padding: 8px 14px;
|
|
257
|
+
font-size: 14px;
|
|
258
|
+
font-weight: 600;
|
|
259
|
+
cursor: pointer;
|
|
260
|
+
}
|
|
261
|
+
.btn:disabled {
|
|
262
|
+
opacity: 0.55;
|
|
263
|
+
cursor: not-allowed;
|
|
264
|
+
}
|
|
265
|
+
.btn-primary {
|
|
266
|
+
background: var(--accent);
|
|
267
|
+
color: var(--accent-contrast);
|
|
268
|
+
}
|
|
269
|
+
.btn-ghost {
|
|
270
|
+
background: transparent;
|
|
271
|
+
color: var(--text);
|
|
272
|
+
border-color: var(--border);
|
|
273
|
+
}
|
|
274
|
+
.btn-small {
|
|
275
|
+
padding: 5px 10px;
|
|
276
|
+
font-size: 13px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.review {
|
|
280
|
+
display: flex;
|
|
281
|
+
flex-direction: column;
|
|
282
|
+
gap: 12px;
|
|
283
|
+
padding-top: 4px;
|
|
284
|
+
}
|
|
285
|
+
.review-line,
|
|
286
|
+
.balance-line,
|
|
287
|
+
.hint-line {
|
|
288
|
+
font-size: 14px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.state {
|
|
292
|
+
padding: 16px;
|
|
293
|
+
border-radius: 10px;
|
|
294
|
+
font-size: 14px;
|
|
295
|
+
display: flex;
|
|
296
|
+
flex-direction: column;
|
|
297
|
+
gap: 4px;
|
|
298
|
+
}
|
|
299
|
+
.state-empty {
|
|
300
|
+
background: var(--surface);
|
|
301
|
+
color: var(--muted);
|
|
302
|
+
}
|
|
303
|
+
.state-error {
|
|
304
|
+
background: var(--error-bg);
|
|
305
|
+
color: var(--error-text);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.skeleton {
|
|
309
|
+
display: flex;
|
|
310
|
+
flex-direction: column;
|
|
311
|
+
gap: 10px;
|
|
312
|
+
}
|
|
313
|
+
.skeleton-row {
|
|
314
|
+
height: 40px;
|
|
315
|
+
border-radius: 10px;
|
|
316
|
+
background: linear-gradient(90deg, var(--surface), var(--border), var(--surface));
|
|
317
|
+
background-size: 200% 100%;
|
|
318
|
+
animation: shimmer 1.2s ease-in-out infinite;
|
|
319
|
+
}
|
|
320
|
+
@keyframes shimmer {
|
|
321
|
+
0% {
|
|
322
|
+
background-position: 200% 0;
|
|
323
|
+
}
|
|
324
|
+
100% {
|
|
325
|
+
background-position: -200% 0;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.issuer-summary {
|
|
330
|
+
display: flex;
|
|
331
|
+
align-items: flex-start;
|
|
332
|
+
justify-content: space-between;
|
|
333
|
+
gap: 12px;
|
|
334
|
+
flex-wrap: wrap;
|
|
335
|
+
}
|
|
336
|
+
.supply {
|
|
337
|
+
display: flex;
|
|
338
|
+
flex-direction: column;
|
|
339
|
+
align-items: flex-end;
|
|
340
|
+
gap: 2px;
|
|
341
|
+
}
|
|
342
|
+
.freeze {
|
|
343
|
+
display: flex;
|
|
344
|
+
flex-direction: column;
|
|
345
|
+
gap: 10px;
|
|
346
|
+
}
|
|
347
|
+
.alloc-head {
|
|
348
|
+
display: flex;
|
|
349
|
+
align-items: center;
|
|
350
|
+
justify-content: space-between;
|
|
351
|
+
gap: 10px;
|
|
352
|
+
flex-wrap: wrap;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.footer {
|
|
356
|
+
max-width: 1100px;
|
|
357
|
+
margin: 0 auto;
|
|
358
|
+
padding: 24px;
|
|
359
|
+
color: var(--muted);
|
|
360
|
+
font-size: 13px;
|
|
361
|
+
border-top: 1px solid var(--border);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* DvP leg layout */
|
|
365
|
+
.leg-actions {
|
|
366
|
+
display: flex;
|
|
367
|
+
flex-direction: column;
|
|
368
|
+
gap: 12px;
|
|
369
|
+
width: 100%;
|
|
370
|
+
}
|
|
371
|
+
.leg-action {
|
|
372
|
+
display: flex;
|
|
373
|
+
flex-direction: column;
|
|
374
|
+
gap: 8px;
|
|
375
|
+
padding: 10px;
|
|
376
|
+
border: 1px solid var(--border);
|
|
377
|
+
border-radius: 10px;
|
|
378
|
+
}
|
|
379
|
+
.leg-status {
|
|
380
|
+
display: flex;
|
|
381
|
+
align-items: center;
|
|
382
|
+
justify-content: space-between;
|
|
383
|
+
gap: 10px;
|
|
384
|
+
flex-wrap: wrap;
|
|
385
|
+
}
|