@cognitiveproof/c2pa-react-ddex-component 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cognitive Proof
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 ADDED
@@ -0,0 +1,178 @@
1
+ # @cognitiveproof/c2pa-react-ddex-component
2
+
3
+ A plugin for [c2pa-react-component](https://github.com/matthewrappard/c2pa-react-component) that renders [DDEX (Digital Data Exchange)](https://ddex.net/) release data — party, release, resource, and deal information carried in a C2PA manifest's `org.mixotron.ddex` custom claim — at three progressive levels of detail.
4
+
5
+ ## Requirements
6
+
7
+ This package is a plugin for `c2pa-react-component`. Install both:
8
+
9
+ ```bash
10
+ npm install @cognitiveproof/c2pa-react-ddex-component c2pa-react-component
11
+ ```
12
+
13
+ ### Peer dependencies
14
+
15
+ | Package | Version |
16
+ |---|---|
17
+ | `react` | `^18.0.0 \|\| ^19.0.0` |
18
+ | `react-dom` | `^18.0.0 \|\| ^19.0.0` |
19
+
20
+ ## CSS
21
+
22
+ Import the stylesheet once at the root of your app:
23
+
24
+ ```ts
25
+ import "@cognitiveproof/c2pa-react-ddex-component/style.css";
26
+ ```
27
+
28
+ **Next.js App Router** — add it to `app/layout.tsx`:
29
+
30
+ ```tsx
31
+ import "@cognitiveproof/c2pa-react-ddex-component/style.css";
32
+
33
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
34
+ return (
35
+ <html>
36
+ <body>{children}</body>
37
+ </html>
38
+ );
39
+ }
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ Pass `DDEXManifest` as a plugin to the `C2paManifest` component from `c2pa-react-component`:
45
+
46
+ ```tsx
47
+ import { C2paManifest } from "c2pa-react-component";
48
+ import { DDEXManifest } from "@cognitiveproof/c2pa-react-ddex-component";
49
+ import type { VerificationOutcome } from "c2pa-react-component-types";
50
+ import "@cognitiveproof/c2pa-react-ddex-component/style.css";
51
+
52
+ export function MediaCard({ outcome }: { outcome: VerificationOutcome }) {
53
+ return (
54
+ <C2paManifest
55
+ manifest={outcome}
56
+ plugin={[DDEXManifest]}
57
+ />
58
+ );
59
+ }
60
+ ```
61
+
62
+ ## Component
63
+
64
+ ### `DDEXManifest`
65
+
66
+ Renders `org.mixotron.ddex` DDEX release data from a C2PA manifest at a configurable level of detail. Returns `null` when the active manifest has no `org.mixotron.ddex` assertion.
67
+
68
+ ```tsx
69
+ import { DDEXManifest } from "@cognitiveproof/c2pa-react-ddex-component";
70
+
71
+ <DDEXManifest manifest={verificationOutcome} level={1} />
72
+ ```
73
+
74
+ #### Props
75
+
76
+ | Prop | Type | Default | Description |
77
+ |---|---|---|---|
78
+ | `manifest` | `VerificationOutcome` | required | Verification result from the C2PA SDK |
79
+ | `level` | `1 \| 2 \| 3` | `1` | Initial disclosure level |
80
+ | `className` | `string` | — | CSS class applied to the root element |
81
+
82
+ #### Disclosure levels
83
+
84
+ | Level | What is shown |
85
+ |---|---|
86
+ | `1` | Compact card: release title, artist(s), genre and parental-warning badges, "More Info" button |
87
+ | `2` | Extends level 1 with label, UPC, ℗/© line, track list (with ISRC), and deal summaries (commercial model, use type, price, territory, validity) |
88
+ | `3` | Full breakdown of the DDEX message — release, all tracks with resolved per-track artists, all deals with full territory/pricing/validity detail, the full party list, and message header (message ID and created timestamp) |
89
+
90
+ Clicking "More Info" advances from level 1 → 2 → 3. Clicking "Small View" at level 3 returns to level 1.
91
+
92
+ ## Data shape
93
+
94
+ The plugin reads the `org.mixotron.ddex` assertion, which mirrors a simplified DDEX ERN message:
95
+
96
+ ```ts
97
+ type DdexAssertion = {
98
+ MessageHeader?: { MessageId?: string; MessageCreatedDateTime?: string };
99
+ PartyList?: { PartyReference?: string; PartyName?: { FullName?: string } }[];
100
+ ReleaseList?: { Release?: DdexRelease[] };
101
+ ResourceList?: { SoundRecording?: DdexSoundRecording[] };
102
+ DealList?: { ReleaseDeal?: DdexReleaseDeal[] };
103
+ };
104
+ ```
105
+
106
+ `DisplayArtist` entries reference parties by `ArtistPartyReference`, and releases reference their tracks by `ReleaseResourceReferenceList` (matched against each `SoundRecording`'s `ResourceReference`); the plugin resolves both automatically. See `examples/` for full sample manifests, including a single-track release, a multi-artist album with multiple deals, and a minimal manifest with no party or deal data.
107
+
108
+ ## Converting between JSON and XML
109
+
110
+ The package also exports the JSON↔XML conversion used by the L3 "Download XML" button, so consumers can convert DDEX files to whichever format they need without depending on the `DDEXManifest` component at all:
111
+
112
+ ```ts
113
+ import { ddexAssertionToXml, xmlToDdexAssertion } from "@cognitiveproof/c2pa-react-ddex-component";
114
+ import type { DdexAssertion } from "@cognitiveproof/c2pa-react-ddex-component";
115
+
116
+ // JSON -> a simplified DDEX ERN NewReleaseMessage XML document
117
+ const xml: string = ddexAssertionToXml(assertion);
118
+
119
+ // XML -> the same JSON assertion shape
120
+ const roundTripped: DdexAssertion = xmlToDdexAssertion(xml);
121
+ ```
122
+
123
+ - `ddexAssertionToXml` has no environment requirements — it only builds a string.
124
+ - `xmlToDdexAssertion` parses with `DOMParser`, so it needs a browser or a DOM-compatible environment (e.g. jsdom in tests); it throws if `DOMParser` isn't available, and throws with a message containing `"Invalid DDEX XML"` if the input doesn't parse.
125
+ - Both functions omit absent sections/fields rather than emitting empty elements, so `xmlToDdexAssertion(ddexAssertionToXml(assertion))` round-trips back to a structurally equal `assertion` for any shape this project renders — see `src/server/ddex/convert.test.ts`.
126
+
127
+ ## Types
128
+
129
+ Types are provided by the shared `c2pa-react-component-types` package, which is installed automatically as a dependency.
130
+
131
+ ```ts
132
+ import type {
133
+ VerificationOutcome,
134
+ Manifest,
135
+ ManifestStore,
136
+ PluginC2PA,
137
+ } from "c2pa-react-component-types";
138
+ ```
139
+
140
+ This package also exports its own DDEX-related types (`DdexAssertion`, `DdexRelease`, `DdexSoundRecording`, `DdexReleaseDeal`, etc.) and helper functions (`getDdexAssertion`, `getArtistNames`, `getSoundRecordingsForRelease`, `formatPrice`, and more) — see [`src/components/Ddex/ddexAssertion.ts`](src/components/Ddex/ddexAssertion.ts).
141
+
142
+ ## Local development
143
+
144
+ Use [yalc](https://github.com/wclr/yalc) to consume the library in another local project without publishing to npm.
145
+
146
+ **Install yalc globally (once):**
147
+
148
+ ```bash
149
+ npm install -g yalc
150
+ ```
151
+
152
+ **Start watch mode in this repo:**
153
+
154
+ ```bash
155
+ # Terminal 1 — rebuild on every save
156
+ npm run dev:lib
157
+
158
+ # Terminal 2 — push updates to yalc's local store
159
+ yalc push --watch
160
+ ```
161
+
162
+ **In your consuming project:**
163
+
164
+ ```bash
165
+ yalc add @cognitiveproof/c2pa-react-ddex-component
166
+ npm install
167
+ ```
168
+
169
+ **Revert to the published npm version:**
170
+
171
+ ```bash
172
+ yalc remove @cognitiveproof/c2pa-react-ddex-component
173
+ npm install
174
+ ```
175
+
176
+ ## License
177
+
178
+ MIT © [Cognitive Proof](https://github.com/Cognitive-Proof) — see [LICENSE](LICENSE).
@@ -0,0 +1,2 @@
1
+ .ddex-card{background:#fff;border-radius:12px;width:95%;padding:16px;display:inline-block;box-shadow:0 4px 20px #00000014}.ddex-header{align-items:center;gap:8px;margin-bottom:16px;display:flex}.ddex-mark{flex-shrink:0;justify-content:center;align-items:center;width:24px;height:24px;display:flex}.ddex-mark svg{width:100%;height:100%;display:block}.ddex-header-title{color:#333;font-size:14px;font-weight:600}.ddex-container{align-items:center;gap:16px;display:flex}.ddex-square{background-color:#eef2ff;border:1px solid #c7d2fe;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:48px;height:48px;display:flex}.ddex-logo-text{color:#3730a3;letter-spacing:-.5px;font-size:16px;font-weight:700}.ddex-summary{flex-direction:column;min-width:0;display:flex}.ddex-media-title{color:#333;overflow-wrap:anywhere;font-size:20px;font-weight:600}.ddex-artist-name{color:#666;font-size:14px}.ddex-thumbnail{object-fit:cover;border-radius:4px;width:48px;height:48px}.ddex-action{margin-top:16px}.ddex-button{color:#666;cursor:pointer;background:#fff;border:2px solid #666;border-radius:999px;width:100%;height:42px;font-size:16px;font-weight:700;transition:background-color .16s,border-color .16s,color .16s,box-shadow .16s}.ddex-button:hover{color:#fff;background:#111;border-color:#111;box-shadow:0 4px 12px #00000024}.ddex-button:focus-visible{outline-offset:3px;outline:3px solid #11111140}.ddex-button-secondary{color:#3730a3;border-color:#c7d2fe}.ddex-button-secondary:hover{color:#fff;background:#3730a3;border-color:#3730a3}.ddex-button-secondary:focus-visible{outline-color:#3730a340}.ddex-section-title{color:#333;margin-bottom:6px;font-size:16px;font-weight:600}.ddex-empty{color:#9a3412;background:#fff7ed;border:1px solid #fed7aa;border-radius:8px;margin-top:16px;padding:10px 12px;font-size:14px}.ddex-key-value{margin-top:12px}.ddex-key-value-label{color:#64748b;text-transform:uppercase;letter-spacing:.025em;margin-bottom:4px;font-size:12px;font-weight:700}.ddex-key-value-value{color:#1e293b;overflow-wrap:anywhere;font-size:15px;line-height:1.5}.ddex-divider{background:#e2e8f0;height:1px;margin:16px 0}.ddex-badges{flex-wrap:wrap;gap:6px;margin-top:12px;display:flex}.ddex-badge{color:#475569;letter-spacing:.02em;background:#f0f4f8;border:1px solid #cbd5e1;border-radius:999px;align-items:center;padding:2px 10px;font-size:12px;font-weight:600;display:inline-flex}.ddex-badge-warning{color:#991b1b;background:#fee2e2;border-color:#fecaca}.ddex-track-list{flex-direction:column;gap:8px;margin-top:10px;display:flex}.ddex-track-item{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:10px 12px}.ddex-track-title{color:#1e293b;font-size:14px;font-weight:600}.ddex-track-isrc{color:#64748b;margin-top:2px;font-size:12px}.ddex-track-meta{color:#64748b;flex-wrap:wrap;gap:10px;margin-top:2px;font-size:12px;display:flex}.ddex-deal-list{flex-direction:column;gap:10px;margin-top:10px;display:flex}.ddex-deal-item{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:10px 12px}.ddex-deal-row{flex-wrap:wrap;gap:6px;display:flex}.ddex-deal-meta{color:#475569;flex-wrap:wrap;gap:10px;margin-top:6px;font-size:13px;display:flex}.ddex-party-list{flex-direction:column;gap:6px;margin-top:10px;display:flex}.ddex-party-item{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;justify-content:space-between;align-items:center;padding:8px 12px;font-size:13px;display:flex}.ddex-party-name{color:#1e293b;font-weight:500}.ddex-party-ref{color:#94a3b8;font-size:12px}
2
+ /*$vite$:1*/