@matterdev/credit 0.0.0-stage → 0.2.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/AGENTS.md ADDED
@@ -0,0 +1,22 @@
1
+ # Agent guide: adding the Matter credit badge
2
+
3
+ Every product Matter Development builds shows a "Developed by Matter Development" credit, usually in the footer
4
+ (websites) or the About/Settings screen (apps). Use this package for it; do not hand-roll the badge or copy logo files.
5
+
6
+ 1. Install: `pnpm add @matterdev/credit` (use the repo's own package manager if its lockfile says otherwise).
7
+ 2. Pick the entry for the stack:
8
+ - Next.js / Vite / Electron → `import { MatterCredit } from "@matterdev/credit/react"`
9
+ - Expo / React Native → `import { MatterCredit } from "@matterdev/credit/native"`
10
+ - Astro → `import { renderCreditHTML } from "@matterdev/credit"` then `<Fragment set:html={renderCreditHTML({ source })} />`
11
+ - Plain HTML → `<matter-credit source="…">` with `import "@matterdev/credit/element"` or the jsDelivr script
12
+ 3. Always pass `source` = the product's slug (e.g. `"optimo"`, `"pockly"`). It is how leads are attributed.
13
+ 4. Matter's own products (Optimo, Pockly, …) pass `kind="product"` so the badge reads "A product of"; client work
14
+ keeps the default "Developed by". Ask if it is unclear whose product it is.
15
+ 5. Leave `theme` at its default unless the design needs otherwise (`inherit` on web follows the site's text colour).
16
+ 6. Electron: pass `onOpen={(url, e) => { e.preventDefault(); openExternal(url); }}`.
17
+ 7. If the site has a strict CSP, allow `img-src data:` and inline styles (see README).
18
+ 8. Look at the result in both light and dark mode before calling it done.
19
+
20
+ Placement conventions: website footer bottom row, centred (variant `stacked`); compact app footers or tight rows
21
+ use `inline` or `mark`; products in another language pass its `locale`: `fr`, `ar`, `es`, `pt`, `de`, `no`, `zh`,
22
+ `ja` or `ko`.
package/README.md CHANGED
@@ -1,3 +1,154 @@
1
- # Temporary Holding Version
1
+ # @matterdev/credit
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
+ The **"Developed by Matter Development"** badge for every product we ship, on every platform we build for.
4
+ One install, no CSS setup, follows the host's light/dark theme, and tags its link so leads on
5
+ matterdevelopment.com show which client site sent them.
6
+
7
+ ```bash
8
+ pnpm add @matterdev/credit
9
+ ```
10
+
11
+ | Stack | Import | Usage |
12
+ |---|---|---|
13
+ | Next.js, Vite, Electron (React) | `@matterdev/credit/react` | `<MatterCredit source="optimo" />` |
14
+ | Expo / React Native | `@matterdev/credit/native` | `<MatterCredit source="optimo" />` |
15
+ | Astro (server-rendered, zero JS) | `@matterdev/credit` | `<Fragment set:html={renderCreditHTML({ source: "optimo" })} />` |
16
+ | Plain HTML / any framework | `@matterdev/credit/element` | `<matter-credit source="optimo"></matter-credit>` |
17
+
18
+ ## Examples
19
+
20
+ ### Next.js / Vite (React)
21
+
22
+ ```tsx
23
+ import { MatterCredit } from "@matterdev/credit/react";
24
+
25
+ <footer>
26
+ <MatterCredit source="optimo" />
27
+ </footer>
28
+ ```
29
+
30
+ The component carries `"use client"`, so it drops into App Router server components as is.
31
+
32
+ ### Electron
33
+
34
+ Links in an Electron renderer should open in the user's browser, not a new app window:
35
+
36
+ ```tsx
37
+ <MatterCredit
38
+ source="optimo-app"
39
+ onOpen={(url, event) => {
40
+ event.preventDefault();
41
+ window.electron.openExternal(url);
42
+ }}
43
+ />
44
+ ```
45
+
46
+ ### Expo / React Native
47
+
48
+ ```tsx
49
+ import { MatterCredit } from "@matterdev/credit/native";
50
+
51
+ <MatterCredit source="pockly" /> // follows the device theme
52
+ <MatterCredit source="pockly" color={colors.muted} /> // or your theme's text colour
53
+ ```
54
+
55
+ No native modules: just `Image` + `Linking`, so it works in Expo Go and needs no rebuild.
56
+ To open in an in-app browser instead:
57
+ `onOpen={(url, e) => { e.preventDefault(); WebBrowser.openBrowserAsync(url); }}`.
58
+
59
+ ### Astro
60
+
61
+ ```astro
62
+ ---
63
+ import { renderCreditHTML } from "@matterdev/credit";
64
+ ---
65
+ <footer>
66
+ <Fragment set:html={renderCreditHTML({ source: "optimo" })} />
67
+ </footer>
68
+ ```
69
+
70
+ Rendered on the server: no JavaScript ships, nothing shifts when the page loads.
71
+
72
+ ### Plain HTML
73
+
74
+ ```html
75
+ <script defer src="https://cdn.jsdelivr.net/npm/@matterdev/credit@0.2.0/dist/element.global.js"
76
+ integrity="sha384-…" crossorigin="anonymous"></script>
77
+ <matter-credit source="client-site" variant="inline"></matter-credit>
78
+ ```
79
+
80
+ Pin the version, and copy the `integrity` hash from the file's page on jsdelivr.com, so a CDN compromise can't
81
+ inject script into the client's site.
82
+
83
+ With a bundler, `import "@matterdev/credit/element"` registers the same tag. Attributes:
84
+ `source variant theme size locale kind label nofollow no-animate href utm-medium utm-campaign utm-content`.
85
+ Listen for `matter-credit:open` (cancelable) to intercept the click.
86
+
87
+ ## Options
88
+
89
+ | Prop | Default | |
90
+ |---|---|---|
91
+ | `source` | page hostname (web) · **required** (native) | Client/product slug → `utm_source`. Normalised: `"Optimo App"` → `optimo-app`. |
92
+ | `variant` | `"stacked"` | `stacked` (label above logo), `inline` (one line), `mark` (M symbol only; label becomes a tooltip). |
93
+ | `theme` | `"inherit"` web · `"auto"` native | See below. |
94
+ | `size` | `"md"` | `sm` / `md` / `lg`, or the logo height in px. |
95
+ | `locale` | `"en"` | `en`, `fr`, `ar` (right-to-left), `es`, `pt`, `de`, `no` (Bokmål), `zh` (Simplified), `ja`, `ko`. |
96
+ | `kind` | `"developed"` | `developed` ("Developed by") for client work; `product` ("A product of") for Matter's own products. |
97
+ | `label` | per locale and `kind` | Replaces the label text, in the visible text and the accessible name. |
98
+ | `nofollow` | `false` | Adds `rel="nofollow"`. By default the link passes SEO value to matterdevelopment.com. |
99
+ | `utm` | — | `{ medium, campaign, content, ...extra }` overrides. |
100
+ | `animate` | `true` | Hover sheen + arrow nudge. Always off under `prefers-reduced-motion`. |
101
+ | `onOpen` | — | `(url, event) => void`. Call `event.preventDefault()` to open the link yourself. |
102
+ | `href` | `https://matterdevelopment.com/` | Destination (http/https only). |
103
+ | `className`, `style` | — | Web only, merged onto the `<a>`. |
104
+ | `color` | — | Native only: ink colour. |
105
+
106
+ ### Choosing a theme
107
+
108
+ - **`inherit`** (web default): the badge is painted in the surrounding text colour, so it follows *whatever* theme
109
+ system the site uses — Tailwind `.dark`, `data-theme`, a toggle, or the OS. Almost always the right choice.
110
+ - **`auto`**: follows the visitor's OS setting. Use it only when the site itself does; on a light-only site an
111
+ OS-dark visitor would get light ink on a light page.
112
+ - **`light` / `dark`**: name the *background* the badge sits on. `dark` = light ink.
113
+
114
+ Fine-tune with CSS custom properties on any ancestor: `--matter-credit-color`, `--matter-credit-label-color`,
115
+ `--matter-credit-font`, `--matter-credit-font-rtl`.
116
+
117
+ ## Tracking
118
+
119
+ Every click lands on
120
+
121
+ ```
122
+ https://matterdevelopment.com/?utm_source=<source>&utm_medium=credit_badge&utm_campaign=developed_by&utm_content=<variant>
123
+ ```
124
+
125
+ (`utm_medium=app_credit` from native apps). matterdevelopment.com stores `utm_*` on contact-form leads and in PostHog.
126
+ The link uses `rel="noopener"` without `noreferrer`, so the referring page is reported too.
127
+
128
+ ## SEO: who built this site
129
+
130
+ ```tsx
131
+ import { MatterCreditJsonLd } from "@matterdev/credit/react";
132
+ <MatterCreditJsonLd name="Optimo" url="https://optimo.app" />
133
+ ```
134
+
135
+ Or `creatorJsonLdScript({ name, url })` for an HTML string. If the site already has a JSON-LD `WebSite` node,
136
+ add `creator: MATTER_ORGANIZATION` to it instead of emitting a second one.
137
+
138
+ ## Content Security Policy
139
+
140
+ The logo is inlined as a `data:` image used as a CSS mask, and the web renderers include a `<style>` element.
141
+ A strict CSP needs `img-src data:` and `style-src 'unsafe-inline'` (or a nonce). The custom element uses shadow DOM
142
+ styles, which follow the same rule.
143
+
144
+ ## Developing
145
+
146
+ ```bash
147
+ pnpm install
148
+ pnpm test # vitest: core, React, custom element, React Native (via react-native-web)
149
+ pnpm typecheck
150
+ pnpm demo # builds, renders every variant to demo/index.html, screenshots light + dark
151
+ pnpm assets # regenerate inlined logos after replacing a file in assets/ (needs Python + Pillow)
152
+ ```
153
+
154
+ Releasing: bump `version`, then `pnpm publish` (runs typecheck, tests and build first).
@@ -0,0 +1,86 @@
1
+ import {
2
+ resolveCredit
3
+ } from "./chunk-N6BHBXMD.js";
4
+
5
+ // src/core/assets.generated.ts
6
+ var WORDMARK_MASK = "data:image/webp;base64,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";
7
+ var MARK_MASK = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0ODIuNzIgMzUxLjY4Ij48cGF0aCBkPSJNNzYuNTQsMzQ5LjY3YzIuMzktLjcyLDQuMTUtMS40NSw1Ljg1LTIuMTcsNS4xNC0yLjE2LDEzLjAxLTUuNTQsMjAuMjgtMTIuODMsNC45My00Ljk0LDcuODQtOS45Miw5LjQ3LTEyLjc1LDAsMCwxLjcyLTIuOTksMy4yMS02LjMxLDE0LjAyLTMxLjMyLDExLjQtNzQuNjksMTEuNC03NC42OS0xLjc1LTI0LjUyLTYuNTktNDYuNTQtMTUuOTgtNzIuNTYtNC45My0xMy42Ni00LjctMTMuMTYtMTkuOTYtNDUuMjItNC44OS0xMC4zMS04LjI3LTE3LjYtMTAuNDEtMjIuNjItMS42Ni0zLjg5LTUuNC0xNC4zOS03LjU0LTIxLjE4LS41LTEuNTgtLjk5LTMuMzQtMS40Ni01LjY1LS45Ny00Ljc5LTEuMDMtOC4yNy0xLjA5LTE0LjA0LS4xLTkuMjksLjc0LTEyLjQ1LDEuMjktMTQuMjIsLjE5LS42MSwuNTktMS44NiwxLjI3LTMuMzMsNi4yMS0xMy40OSwxNC44LTE4LjA5LDI5LjIyLTE1LjYsNy44NSwxLjM1LDEyLDMuMTEsMjAuNzMsOC43OCwxMy40Nyw4Ljc3LDMwLjQ5LDI4LjQ1LDQ0Ljk0LDUyLjA1LDIuNzksNC41Niw0Ljc5LDguMDYsNi4xMiwxMC41NywyLjgsNS4yOSw0LjQ2LDkuMTQsNy42MywxNi41NSw0LjU2LDEwLjY1LDYuODQsMTUuOTcsOC4xNiwyMC40NSwuOTIsMy4xMSwyLjI3LDYuOTUsNC45OCwxNC42MiwuMTEsLjMyLC41MywxLjMxLDEuMzgsMy4yOCwxLjA3LDIuNSwxLjMxLDIuOTcsMS44LDMuMTgsMS4yLC41MSwyLjg0LS45Myw0LjM0LTIuMjMsMy4wNi0yLjYzLDYuMzEtNS4wNSw5LjMyLTcuNzMsNS41OC00Ljk3LDYuNDEtNS41Miw2Ljc4LTcuMjEsLjQyLTEuODktLjE4LTMuMTctMS45OC04LjAxLTIuODctNy43Mi0yLjM1LTYuMi0zLjU1LTEwLjAzLTQuNjMtMTQuODctMjIuNDMtNDguOC0zNS4yOS02Ny4zLTYuNS05LjM2LTEzLjgtMTcuODYtMjEuNTEtMjUuMDJDMTM2LjQxLDEwLjI1LDEyNC4wNSwzLjU0LDEwNC44OSwuNzJjLTExLjY3LTEuNzMtMjIuNTMtLjM2LTMzLjM5LDQuMi0xMC4yLDQuMjctMTYuMDgsOS44OS0yMS4yNywyMC4zNC04LjU0LDE3LjI2LTguOCwzNy4xMS0uOCw2MS43OSw0Ljk1LDE1LjMsNi43MSwxOS41OSwxNC4xNiwzNC42NywxMS42MiwyMy41OSwxNS4xMywzMS44MiwyMS4zNyw1MC40MSwyLjYxLDcuNzgsNS4xOSwxNi4yNiw1Ljc0LDE4Ljg1czIuMDYsOS4zNywzLjM1LDE1LjA4YzQuNjksMjAuNjUsNi4zOCwzNS4zNSw2LjI5LDU0LjY1LS4xMiwyNC41Ny0yLjUxLDM2LjU5LTkuNzUsNDkuMTMtMi4xOSwzLjc5LTQuNTEsNi40OS01LjEsNy4xMi00LjU5LDQuOTMtOS4yMiw2LjkzLTEwLjE0LDcuMzItLjU5LC4yNS0yLjU1LDEuMDUtNS4yNSwxLjYxLS43NywuMTYtMy41NywuNjMtNy42NiwuNi04LjczLS4wNS0xMy4wOS0xLjkyLTIwLjAxLTguNjEtOS41NS05LjIzLTEzLjI1LTIxLjUxLTE1Ljc5LTUyLjQ1LTIuMTEtMjUuNTksMy4zNS01OS4zOCwxMy41Ny04My45MSw2LjM1LTE1LjIyLDEyLjM1LTI4LjI5LDE0LjAxLTMwLjQ3LC45Ny0xLjI3LDEuNS0yLjMxLDEuNTctMy40LC4wNy0xLjE3LS4yNi0yLjE5LTMuMDItNi4yMy0yLjU0LTMuNzItMi4xNC0yLjctNS4yNC02Ljk2LTEuNjEtMi4yMS0xLjkyLTIuNzYtMi42OC0yLjg4LTEuODMtLjI4LTMuNjMsMi4zNS01Ljk2LDUuODQtNC45MSw3LjM2LTcuMzcsMTEuMDQtNy4zOCwxMS4wNy0xMC4wMywxNC45MS0xOSwzMi45OC0yMi45OCw0Ni4yNkMxLjY5LDIxNy42MSwuMTgsMjI4LjQ3LDAsMjU1LjQ2Yy0uMTcsMjkuMTksMi40Niw0NC4wMywxMS4xLDYyLjc0LDQuMDYsOC43Nyw4LjgyLDE1LjQ0LDE0Ljg3LDIwLjU1LDcuMTMsNi4wMiwxNC4xMiw4LjQ4LDE3LjcxLDkuNyw0LjAyLDEuMzYsMTcuNjQsNS43OCwzMi44NSwxLjIyWiIvPjxwYXRoIGQ9Ik0yNTcuMjQsMzI2LjU0YzcuMDItMi40NCwxNy40My04LjY1LDIxLjUxLTEyLjg1LDkuNTEtOS43NSwxNi45NS0yNS41MiwxOS44NS00Mi4wNCwyLjQ3LTE0LjIsMS41NC0zNi42My0yLjMtNTQuMjgtNC4yOS0xOS43NC0xMS42Ni0zOS42LTE3LjI5LTM5LjEtLjg1LC4wOC0xLjE2LC41Ny01LjY5LDQuMDEtMy42MiwyLjc0LTYuNjEsNC44NC04LjUsNi4xNC0xLjA2LDEuMDItMi4xMiwyLjA1LTMuMTgsMy4wNywxLjQ3LDQuMDcsMi45NCw4LjE0LDQuNDEsMTIuMjEsMy41NiwxMC43Miw0LjkxLDE1Ljc0LDcuNjYsMjguMjYsLjUyLDIuMzMsLjk5LDEwLjE5LDEuMDQsMTcuNDMsLjA5LDEwLjk2LS4yOCwxNC42My0yLjA4LDIxLjY3LTUuMTUsMTkuOTctMTEuOTEsMjguNTktMjYuMjIsMzMuMzktMTUuNjUsNS4yNy0yOS45NS0uNTUtMzcuMDktMTUuMDgtNi43NC0xMy44LTUuNC00NC43OSwyLjg0LTY0LjQyLDEwLjY0LTI1LjMyLDI3LjQ0LTQ4LjAxLDQ0LjQzLTU5Ljk1LDIuODUtMi4wMSw3LjUyLTUuMzMsMTAuMzgtNy40LDIwLjQ2LTE0Ljg1LDQ4LjQ0LTI0LjE2LDc2LjA3LTI1LjI4LDcuNDItLjI5LDEyLjgxLS45MiwxMy4zMi0xLjU0LC40NS0uNTcsMS4wNC01LjAxLDEuMy05Ljg3LC42Ni0xMS45MywuMjEtMTIuMzYtMTIuMjQtMTIuMzYtOS42MywwLTIyLjksMS4yNi0zMC44NywyLjk0LTguMDIsMS42OC0xMi44MywzLjI1LTMwLjE0LDkuODYtNy41NiwyLjg3LTI0LjI0LDExLjA3LTMzLjkzLDE2LjY3LTIwLjUxLDExLjg1LTM5Ljc5LDMwLjk5LTUzLjg4LDUzLjUyLTQuMDUsNi40My0xMi41NCwyMy43My0xMi41NCwyNS40OSwwLC41NS0uNjIsMi4zMy0xLjM4LDMuOTgtMi41Niw1LjUyLTQuNDMsMTMuNTQtNS40LDIzLjIyLS4yOSwyLjk0LS4zNSw1Ljk2LS40NywxMS45OS0uMDcsMy40NC0uMTMsNi45NSwwLDExLjU3LC4yMiw3LjQ1LC40LDEyLjcyLDIuMDUsMTkuNSwuNTksMi40NCwxLjE2LDQuMjEsMS41OCw1LjQ4LDIuNDEsNy40Miw0LjY0LDE0LjMsMTAuMzYsMjAuNjMsMy45OSw0LjQyLDguMTcsNi45LDEwLjE3LDguMDcsMy43LDIuMTYsNi45OCwzLjM3LDEwLjkxLDQuODEsMy40OSwxLjI5LDYuMjUsMS45Nyw2Ljk2LDIuMTQsLjQzLC4xLC43OSwuMTksMS4wNiwuMjUsNy44OCwxLjc2LDE0LjEzLDEuNTEsMTguMywxLjMyLDYuODktLjMyLDExLjg1LTEuNDYsMTMuMzgtMS44MywwLDAsMi45My0uNjgsNS42My0xLjYyWiIvPjxwYXRoIGQ9Ik00NDEuMzIsMzQ0Ljg3YzEzLjM4LTUuODgsMTguODMtMTAuMjIsMjYuMTUtMjAuOCw1LjU5LTguMDgsOC44Mi0xNC45OSwxMS40NS0yNC41LDQuODEtMTcuNDMsNS4xMi02MC41OSwuNTktODEuMjktNC4wNS0xOC40Ny0xMS45OC0zOS4zMi0yMC40MS01My42Ni0uOTItMS41Ni0zLjM5LTUuODEtNS41Mi05LjQyLTcuNTctMTIuODYtOS4xNC0xNy41OS0xMS44Ni0xNy40My0xLjU1LC4wOS0xLjYxLDEuNjMtNi44MSw4LjQtNC4xMiw1LjM2LTUuMTgsNS44Mi01LjgsOC4wNi0uNjgsMi40OC0uNzMsNS4wMywzLjA4LDExLjcxLDUuNzksMTAuMTcsOS43NSwxOC4wNSwxMS45NywyMy44MywxLjA1LDIuNzUsMi4yOCw1Ljg1LDIuNzEsNi44OCw0LjE1LDkuNzksOC40NywzMS40NCw5LjY1LDQ4LjMzLDIuODIsNDAuMzYtNS42LDY4LjA2LTIyLjk1LDc1LjU5LTYuODEsMi45Ni0xNy4yMiwzLjYtMjIuOTEsMS40NC0zLjU2LTEuMzUtNS45OC0yLjg2LTguMTgtNC4zOS0yLjItMS41My0xMy41NS05LjcyLTE3LjE4LTI0LjgxLS4zNy0xLjUyLS41Ni0yLjk3LS45Ni01Ljg3LTEuMTUtOC40MS0uODYtMTQuNzYtLjg2LTIyLjkxLDAtMjIuMzksLjQ1LTI2LjI5LDUuNjktNDguNTIsMi45NC0xMi40OSw5LjQ2LTMzLjE5LDEzLjUyLTQyLjg3LDEuNjMtMy44OSwzLjMtOC4xMywzLjc1LTkuNDIsLjQzLTEuMywxLjYzLTQuMDUsMi42Ni02LjEyLDcuMzctMTQuOCwxOC40LTQ0LjQxLDE5LjU4LTUyLjU0LC4zMS0yLjIsMS4yNS02LjU1LDIuMDYtOS42NSwyLjIzLTguNTgsMi4wOS00Mi4yMS0uMjEtNDkuOTQtNC44Mi0xNi4yNy0xMi41OS0yNy4xLTI0LjA3LTMzLjYyLTcuODktNC40OC0xMy43Ny01Ljg0LTI1LjQ2LTUuOS0yNC4zMS0uMTQtNDQuMTgsOS4yNS02NC4xNiwzMC4zLTE3LjA3LDE3Ljk4LTI2LjQ0LDMxLjMtMzYuNDIsNTEuNzQtNi45OSwxNC4zLTEwLDIyLjUtOS40OSwyNS45NywuMjQsMS43MywuODYsMi4xNCwzLjEzLDIuMDksNC43Mi0uMTIsMTcuNzMtMy4yMywyMC42OC00Ljk2LDIuNzctMS41OSw5Ljg3LTExLjMzLDE1LjYzLTIxLjM5LDYuMzUtMTEuMSwxNC44Ny0yMi43NywyNC4yNC0zMy4xNyw3LjA0LTcuODIsMjEuMjktMTkuMiwyNy42Ny0yMi4xLDUuMzYtMi40NCwxNC40NC0zLjg3LDIxLjA2LTMuMzQsMi45LC4yMyw1LjM4LC44Nyw3LjYxLDIuMDMsMS45OCwxLjAzLDUuMzksMy4yMSw4LjY5LDguNzksNi40OSwxMC45NSw2LjU0LDI0LjY0LDYuNDksMjguNDYtLjE3LDEzLjE2LTIuOTcsMjIuOTUtNS4zOCwzMS4xNC0zLjUsMTEuOTEtOC40LDIzLjAzLTE4LjMsNDUuMjItMy44Niw4LjY1LTYuNCwxNC45NC0xMS4zNiwyOC4yNy0uNjcsMS44Mi0yLjE2LDYuNDctMy4yOSwxMC4zNi0xLjEyLDMuODctMi42MSw4Ljk4LTMuMzIsMTEuMzEtLjY3LDIuMzItMS45Miw3LjQyLTIuNzMsMTEuMzEtLjgsMy44Ny0xLjg1LDguNTQtMi4zNSwxMC4zNi0uNSwxLjgyLTEuODMsMTAuOTMtMi45NiwyMC4yNS0zLjEsMjUuNjYtMS43OCw0NS4zNiw0LjMyLDY0LjA5LDEuNjIsNC45OCwzLjY1LDkuNDEsMy42NSw5LjQxLC4zNiwuNzMsMS4wOSwyLjEzLDIuMjEsMy45OSwyLjU1LDQuMjEsNy4yMSwxMS45MSwxNS43OSwxOS4xMSwyLjA3LDEuNzQsNS4yMSw0LjMsOS44Nyw2Ljc4LDIuNTIsMS4zNCw1LjE0LDIuNCw4LjQ5LDMuNTUsMy41NywxLjIyLDYuMzUsMS44MSw2Ljg5LDEuOTMsNS41MywxLjE4LDkuNjUsMS4wOSwxNi4yOCwuOTYsOC4xNS0uMTcsMTEuNTQtMS4wMiwxMy42My0xLjY2LDEuNTItLjQ3LDIuNjItLjg4LDMuNzItMS4zNloiLz48L3N2Zz4=";
8
+
9
+ // src/core/css.ts
10
+ var LIGHT_INK = "#1F2A3B";
11
+ var DARK_INK = "#F3F5F7";
12
+ var CREDIT_CSS = `
13
+ .mdc.mdc{color:var(--matter-credit-color);text-decoration:none;font-family:var(--matter-credit-font,ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace)}
14
+ .mdc.mdc--light{color:var(--matter-credit-color,${LIGHT_INK})}
15
+ .mdc.mdc--dark{color:var(--matter-credit-color,${DARK_INK})}
16
+ .mdc.mdc--auto{color:var(--matter-credit-color,${LIGHT_INK})}
17
+ @media (prefers-color-scheme:dark){.mdc.mdc--auto{color:var(--matter-credit-color,${DARK_INK})}}
18
+ .mdc{display:inline-flex;align-items:center;gap:.6em;vertical-align:middle;line-height:1;border-radius:6px;-webkit-tap-highlight-color:transparent}
19
+ .mdc--stacked{flex-direction:column}
20
+ .mdc--mark{gap:.35em}
21
+ .mdc:focus-visible{outline:2px solid currentColor;outline-offset:4px}
22
+ .mdc__label{font-size:var(--mdc-label-size,11px);font-weight:400;letter-spacing:.14em;text-transform:uppercase;white-space:nowrap;opacity:.7;color:var(--matter-credit-label-color)}
23
+ .mdc--stacked .mdc__label{margin-inline-end:-.14em}
24
+ .mdc[dir=rtl] .mdc__label{letter-spacing:0;margin-inline-end:0;text-transform:none;font-family:var(--matter-credit-font-rtl,system-ui,sans-serif)}
25
+ .mdc__row{display:inline-flex;align-items:center;gap:6px}
26
+ .mdc__logo--wordmark{--mdc-logo:url("${WORDMARK_MASK}")}
27
+ .mdc__logo--mark{--mdc-logo:url("${MARK_MASK}")}
28
+ .mdc__logo{display:block;flex:none;background-color:currentColor;opacity:.85;transition:opacity .2s ease;-webkit-print-color-adjust:exact;print-color-adjust:exact;-webkit-mask-image:var(--mdc-logo);mask-image:var(--mdc-logo);-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}
29
+ .mdc:hover .mdc__logo,.mdc:focus-visible .mdc__logo{opacity:1}
30
+ .mdc__ext{display:block;flex:none;opacity:.6;transition:transform .25s ease,opacity .2s ease}
31
+ .mdc:hover .mdc__ext{opacity:.9}
32
+ .mdc[dir=rtl] .mdc__ext{transform:scaleX(-1)}
33
+ @media (prefers-reduced-motion:no-preference){
34
+ .mdc--animate .mdc__logo{-webkit-mask-image:var(--mdc-logo),linear-gradient(105deg,#000 40%,rgba(0,0,0,.3) 50%,#000 60%);mask-image:var(--mdc-logo),linear-gradient(105deg,#000 40%,rgba(0,0,0,.3) 50%,#000 60%);-webkit-mask-size:100% 100%,300% 100%;mask-size:100% 100%,300% 100%;-webkit-mask-position:0 0,100% 0;mask-position:0 0,100% 0;-webkit-mask-composite:source-in;mask-composite:intersect}
35
+ .mdc--animate:hover .mdc__logo{-webkit-mask-position:0 0,0 0;mask-position:0 0,0 0;transition:opacity .2s ease,-webkit-mask-position .9s ease,mask-position .9s ease}
36
+ .mdc--animate:hover .mdc__ext{transform:translate(2px,-2px)}
37
+ .mdc--animate[dir=rtl]:hover .mdc__ext{transform:scaleX(-1) translate(2px,-2px)}
38
+ }
39
+ @media (forced-colors:active){.mdc__logo{forced-color-adjust:none;background-color:LinkText}}
40
+ `.trim();
41
+ var EXTERNAL_ICON_PATHS = [
42
+ "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",
43
+ "M15 3h6v6M10 14 21 3"
44
+ ];
45
+
46
+ // src/core/html.ts
47
+ var ESCAPES = {
48
+ "&": "&amp;",
49
+ "<": "&lt;",
50
+ ">": "&gt;",
51
+ '"': "&quot;",
52
+ "'": "&#39;"
53
+ };
54
+ function escapeHtml(value) {
55
+ return value.replace(/[&<>"']/g, (c) => ESCAPES[c]);
56
+ }
57
+ function externalIconSvg(size) {
58
+ const paths = EXTERNAL_ICON_PATHS.map((d) => `<path d="${d}"/>`).join("");
59
+ return `<svg class="mdc__ext" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">${paths}</svg>`;
60
+ }
61
+ function renderCreditMarkup(model) {
62
+ const attrs = [
63
+ `class="${model.className}"`,
64
+ `href="${escapeHtml(model.href)}"`,
65
+ `target="_blank"`,
66
+ `rel="${model.rel}"`,
67
+ `dir="${model.dir}"`,
68
+ `lang="${model.lang}"`,
69
+ `aria-label="${escapeHtml(model.ariaLabel)}"`,
70
+ model.title ? `title="${escapeHtml(model.title)}"` : null,
71
+ `style="--mdc-label-size:${model.labelSize}px"`,
72
+ `data-matter-credit=""`
73
+ ].filter(Boolean).join(" ");
74
+ const label = model.label ? `<span class="mdc__label" aria-hidden="true">${escapeHtml(model.label)}</span>` : "";
75
+ const logo = `<span class="mdc__logo mdc__logo--${model.logo.kind}" aria-hidden="true" style="width:${model.logo.width}px;height:${model.logo.height}px"></span>`;
76
+ return `<a ${attrs}>${label}<span class="mdc__row">${logo}${externalIconSvg(model.iconSize)}</span></a>`;
77
+ }
78
+ function renderCreditHTML(options) {
79
+ return `<style data-matter-credit>${CREDIT_CSS}</style>${renderCreditMarkup(resolveCredit(options))}`;
80
+ }
81
+
82
+ export {
83
+ CREDIT_CSS,
84
+ renderCreditMarkup,
85
+ renderCreditHTML
86
+ };
@@ -0,0 +1,118 @@
1
+ // src/core/labels.ts
2
+ var BRAND_NAME = "Matter Development";
3
+ var LOCALES = {
4
+ en: { label: "Developed by", productLabel: "A product of", newTab: "opens in a new tab", opensBrowser: "opens in your browser", dir: "ltr" },
5
+ fr: { label: "D\xE9velopp\xE9 par", productLabel: "Un produit de", newTab: "s\u2019ouvre dans un nouvel onglet", opensBrowser: "s\u2019ouvre dans votre navigateur", dir: "ltr" },
6
+ ar: { label: "\u062A\u0645 \u0627\u0644\u062A\u0637\u0648\u064A\u0631 \u0628\u0648\u0627\u0633\u0637\u0629", productLabel: "\u0623\u062D\u062F \u0645\u0646\u062A\u062C\u0627\u062A", newTab: "\u064A\u0641\u062A\u062D \u0641\u064A \u0639\u0644\u0627\u0645\u0629 \u062A\u0628\u0648\u064A\u0628 \u062C\u062F\u064A\u062F\u0629", opensBrowser: "\u064A\u0641\u062A\u062D \u0641\u064A \u0627\u0644\u0645\u062A\u0635\u0641\u062D", dir: "rtl" },
7
+ es: { label: "Desarrollado por", productLabel: "Un producto de", newTab: "se abre en una pesta\xF1a nueva", opensBrowser: "se abre en tu navegador", dir: "ltr" },
8
+ pt: { label: "Desenvolvido por", productLabel: "Um produto de", newTab: "abre em uma nova aba", opensBrowser: "abre no seu navegador", dir: "ltr" },
9
+ de: { label: "Entwickelt von", productLabel: "Ein Produkt von", newTab: "\xF6ffnet in einem neuen Tab", opensBrowser: "\xF6ffnet im Browser", dir: "ltr" },
10
+ no: { label: "Utviklet av", productLabel: "Et produkt fra", newTab: "\xE5pnes i en ny fane", opensBrowser: "\xE5pnes i nettleseren", dir: "ltr" },
11
+ zh: { label: "\u5F00\u53D1\u8005", productLabel: "\u51FA\u54C1", newTab: "\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00", opensBrowser: "\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00", dir: "ltr" },
12
+ ja: { label: "\u958B\u767A", productLabel: "\u63D0\u4F9B", newTab: "\u65B0\u3057\u3044\u30BF\u30D6\u3067\u958B\u304D\u307E\u3059", opensBrowser: "\u30D6\u30E9\u30A6\u30B6\u3067\u958B\u304D\u307E\u3059", dir: "ltr" },
13
+ ko: { label: "\uAC1C\uBC1C", productLabel: "\uC81C\uACF5", newTab: "\uC0C8 \uD0ED\uC5D0\uC11C \uC5F4\uB9BC", opensBrowser: "\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uB9BC", dir: "ltr" }
14
+ };
15
+ var LOCALE_CODES = Object.keys(LOCALES);
16
+ function localeStrings(locale) {
17
+ return LOCALES[locale ?? "en"] ?? LOCALES.en;
18
+ }
19
+
20
+ // src/core/url.ts
21
+ var MATTER_URL = "https://matterdevelopment.com/";
22
+ function normalizeSource(source) {
23
+ return source.trim().toLowerCase().replace(/^www\./, "").replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
24
+ }
25
+ function safeUrl(href) {
26
+ try {
27
+ const url = new URL(href);
28
+ if (url.protocol === "https:" || url.protocol === "http:") return url;
29
+ } catch {
30
+ }
31
+ return new URL(MATTER_URL);
32
+ }
33
+ function buildCreditUrl(options = {}) {
34
+ const { source, utm = {}, variant, defaultMedium = "credit_badge", href = MATTER_URL } = options;
35
+ const { medium, campaign, content, ...extra } = utm;
36
+ const url = safeUrl(href);
37
+ const set = (key, value) => {
38
+ if (value) url.searchParams.set(key, value);
39
+ };
40
+ set("utm_source", source ? normalizeSource(source) : void 0);
41
+ set("utm_medium", medium ?? defaultMedium);
42
+ set("utm_campaign", campaign ?? "developed_by");
43
+ set("utm_content", content ?? variant);
44
+ for (const [key, value] of Object.entries(extra)) set(key, value);
45
+ return url.toString();
46
+ }
47
+
48
+ // src/core/metrics.generated.ts
49
+ var WORDMARK_RATIO = 4.2755;
50
+ var MARK_RATIO = 1.3726;
51
+
52
+ // src/core/model.ts
53
+ var LOGO_HEIGHT = {
54
+ stacked: { sm: 20, md: 28, lg: 36 },
55
+ inline: { sm: 16, md: 20, lg: 26 },
56
+ mark: { sm: 18, md: 24, lg: 32 }
57
+ };
58
+ var LABEL_SIZE = {
59
+ stacked: { sm: 10, md: 11, lg: 13 },
60
+ inline: { sm: 11, md: 12, lg: 14 },
61
+ mark: { sm: 11, md: 12, lg: 14 }
62
+ };
63
+ var round = (n) => Math.round(n * 100) / 100;
64
+ function sizes(variant, size) {
65
+ if (typeof size === "number" && size > 0) {
66
+ return { height: size, label: Math.min(16, Math.max(10, Math.round(size * 0.42))) };
67
+ }
68
+ const key = size === "sm" || size === "lg" ? size : "md";
69
+ return { height: LOGO_HEIGHT[variant][key], label: LABEL_SIZE[variant][key] };
70
+ }
71
+ function resolveCredit(options = {}, platform = "web") {
72
+ const variant = options.variant ?? "stacked";
73
+ const theme = options.theme ?? "inherit";
74
+ const strings = localeStrings(options.locale);
75
+ const label = options.label?.trim() || (options.kind === "product" ? strings.productLabel : strings.label);
76
+ const { height, label: labelSize } = sizes(variant, options.size);
77
+ const ratio = variant === "mark" ? MARK_RATIO : WORDMARK_RATIO;
78
+ return {
79
+ variant,
80
+ theme,
81
+ href: buildCreditUrl({
82
+ source: options.source,
83
+ utm: options.utm,
84
+ variant,
85
+ defaultMedium: platform === "native" ? "app_credit" : "credit_badge",
86
+ href: options.href
87
+ }),
88
+ rel: options.nofollow ? "noopener nofollow" : "noopener",
89
+ dir: strings.dir,
90
+ lang: options.locale && options.locale in LOCALES ? options.locale : "en",
91
+ label: variant === "mark" ? null : label,
92
+ ariaLabel: `${label} ${BRAND_NAME} (${platform === "native" ? strings.opensBrowser : strings.newTab})`,
93
+ title: variant === "mark" ? `${label} ${BRAND_NAME}` : void 0,
94
+ className: [
95
+ "mdc",
96
+ `mdc--${variant}`,
97
+ `mdc--${theme}`,
98
+ options.animate === false ? null : "mdc--animate"
99
+ ].filter(Boolean).join(" "),
100
+ logo: {
101
+ kind: variant === "mark" ? "mark" : "wordmark",
102
+ width: round(height * ratio),
103
+ height
104
+ },
105
+ labelSize,
106
+ iconSize: Math.max(10, Math.round(variant === "stacked" ? height * 0.46 : labelSize))
107
+ };
108
+ }
109
+
110
+ export {
111
+ BRAND_NAME,
112
+ LOCALES,
113
+ LOCALE_CODES,
114
+ MATTER_URL,
115
+ normalizeSource,
116
+ buildCreditUrl,
117
+ resolveCredit
118
+ };