create-partylayer-app 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/_package.json +4 -3
- package/templates/next-ssr/app/page.tsx +4 -4
- package/templates/next-ssr/app/providers.tsx +16 -6
- package/templates/react-dvp/README.md +128 -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 +133 -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/_package.json +3 -2
- package/templates/react-vite/src/App.tsx +1 -1
- package/templates/react-vite/src/main.tsx +11 -1
- package/templates/vanilla/_package.json +1 -1
- package/templates/vue-nuxt-pinia/_package.json +4 -3
- package/templates/vue-nuxt-pinia/app.vue +2 -2
- package/templates/vue-nuxt-pinia/plugins/partylayer.client.ts +9 -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
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
"lint": "next lint"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@partylayer/react": "^
|
|
14
|
-
"@partylayer/sdk": "^0.
|
|
15
|
-
"@partylayer/session": "^1.1.
|
|
13
|
+
"@partylayer/react": "^2.1.0",
|
|
14
|
+
"@partylayer/sdk": "^0.15.0",
|
|
15
|
+
"@partylayer/session": "^1.1.4",
|
|
16
|
+
"@tanstack/react-query": "^5.0.0",
|
|
16
17
|
"next": "^14.2.5",
|
|
17
18
|
"react": "^18.3.1",
|
|
18
19
|
"react-dom": "^18.3.1"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getServerSession, truncateParty } from '@/lib/session';
|
|
2
2
|
import { ConnectButton } from '@/components/ConnectButton';
|
|
3
3
|
|
|
4
|
-
// Reading cookies() opts this route into dynamic (per-request) rendering
|
|
4
|
+
// Reading cookies() opts this route into dynamic (per-request) rendering, which
|
|
5
5
|
// is exactly what SSR session needs: the server reads the session cookie on
|
|
6
6
|
// every request and renders the connected state into the initial HTML.
|
|
7
7
|
export default function Home() {
|
|
@@ -11,7 +11,7 @@ export default function Home() {
|
|
|
11
11
|
return (
|
|
12
12
|
<main className="app">
|
|
13
13
|
<h1>{{PROJECT_NAME}}</h1>
|
|
14
|
-
<p className="subtitle">A PartyLayer dApp on Canton
|
|
14
|
+
<p className="subtitle">A PartyLayer dApp on Canton: connect any verified wallet.</p>
|
|
15
15
|
|
|
16
16
|
{/* SERVER-RENDERED connected state (Option A: server owns the display). This
|
|
17
17
|
appears in the initial HTML from the cookie, before any client JS, so
|
|
@@ -22,11 +22,11 @@ export default function Home() {
|
|
|
22
22
|
</p>
|
|
23
23
|
) : (
|
|
24
24
|
<p className="session" data-testid="server-cta">
|
|
25
|
-
Not connected
|
|
25
|
+
Not connected: connect a wallet to continue.
|
|
26
26
|
</p>
|
|
27
27
|
)}
|
|
28
28
|
|
|
29
|
-
{/* Interactive client island
|
|
29
|
+
{/* Interactive client island: reconciles silently with the live provider. */}
|
|
30
30
|
<ConnectButton />
|
|
31
31
|
</main>
|
|
32
32
|
);
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useMemo, type ReactNode } from 'react';
|
|
3
|
+
import { useMemo, useState, type ReactNode } from 'react';
|
|
4
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
4
5
|
import { PartyLayerKit } from '@partylayer/react';
|
|
5
6
|
import { createCookieStorage } from '@partylayer/session';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Client provider boundary. cookieStorage on the client uses document.cookie
|
|
9
|
-
* (synchronous
|
|
10
|
-
*
|
|
10
|
+
* (synchronous, flash-free), the SAME cookie the server reads in lib/session.ts,
|
|
11
|
+
* so server HTML and client hydration agree.
|
|
12
|
+
*
|
|
13
|
+
* The QueryClient is created with useState so each browser client gets one stable
|
|
14
|
+
* instance (never shared across server requests), the SSR-safe pattern for the
|
|
15
|
+
* App Router. PartyLayer's data hooks (@partylayer/react/query) read this client;
|
|
16
|
+
* the base session surface works without it, but it is set up so the query hooks
|
|
17
|
+
* are ready when you reach for them.
|
|
11
18
|
*/
|
|
12
19
|
export function Providers({ children }: { children: ReactNode }) {
|
|
13
20
|
const storage = useMemo(() => createCookieStorage(), []);
|
|
21
|
+
const [queryClient] = useState(() => new QueryClient());
|
|
14
22
|
return (
|
|
15
|
-
<
|
|
16
|
-
{
|
|
17
|
-
|
|
23
|
+
<QueryClientProvider client={queryClient}>
|
|
24
|
+
<PartyLayerKit network="devnet" appName="{{PROJECT_NAME}}" sessionOptions={{ storage }}>
|
|
25
|
+
{children}
|
|
26
|
+
</PartyLayerKit>
|
|
27
|
+
</QueryClientProvider>
|
|
18
28
|
);
|
|
19
29
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
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.
|
|
@@ -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.15.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
|
+
}
|