@oksigenia/share 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 Oksigenia SL
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/NOTICE.md ADDED
@@ -0,0 +1,9 @@
1
+ # NOTICE
2
+
3
+ `@oksigenia/share` is derived from the WordPress plugin
4
+ [`oksigenia-share`](https://wordpress.org/plugins/oksigenia-share/)
5
+ (GPLv2+) originally developed by Oksigenia SL.
6
+
7
+ The TypeScript/web-component re-packaging in this directory is
8
+ licensed under MIT (see `../../LICENSE`) per the dual-license intent
9
+ of the original author. The WordPress distribution remains under GPLv2+.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # @oksigenia/share
2
+
3
+ Lightweight, privacy-first social share buttons.
4
+
5
+ - **Zero dependencies.** ~6 KB gzipped including all 9 SVG icons.
6
+ - **No tracking.** No third-party scripts, no cookies, no pings. Just
7
+ the native share-intent URLs of each network, opened on user click.
8
+ - **9 networks**: X (Twitter), Bluesky, Threads, WhatsApp, Telegram,
9
+ LinkedIn, Reddit, Nostr (copy-only), Email.
10
+ - **8 locales**: en, es, it, nl, de, pt, gn (Guaraní), sv.
11
+ - **Two APIs**:
12
+ - Web component `<oksigenia-share>` with Shadow DOM (drop-in, no CSS to load).
13
+ - Imperative helper `mountShare(target, options)` for full control.
14
+ - **A11y**: focus-visible outline, `role="group"`, `aria-label`, `aria-live`
15
+ announcement when Nostr copies, respects `prefers-reduced-motion`.
16
+
17
+ Originally developed by [Oksigenia](https://oksigenia.com) as the
18
+ WordPress plugin
19
+ [`oksigenia-share`](https://wordpress.org/plugins/oksigenia-share/),
20
+ re-packaged as a framework-agnostic web library.
21
+
22
+ ## Install
23
+
24
+ ```sh
25
+ npm i @oksigenia/share
26
+ # or pnpm/yarn
27
+ ```
28
+
29
+ ## Use it as a web component
30
+
31
+ ```html
32
+ <script type="module">
33
+ import '@oksigenia/share/web-component';
34
+ </script>
35
+
36
+ <oksigenia-share
37
+ url="https://granjaoga.com/articulos/vori-vori"
38
+ title="Vori vori paraguayo"
39
+ locale="es-PY"
40
+ networks="x wa tg li em"
41
+ x-handle="granjaoga"
42
+ ></oksigenia-share>
43
+ ```
44
+
45
+ Attributes:
46
+
47
+ | Attribute | Default | Notes |
48
+ |---|---|---|
49
+ | `url` | `location.href` | Absolute URL to share. |
50
+ | `title` | `document.title` | Title/text to share. |
51
+ | `locale` | `navigator.language` | Falls back to base (`es-PY` → `es`) then to `en`. |
52
+ | `networks` | all 9 | Space- or comma-separated subset, in display order. |
53
+ | `x-handle` | – | Without `@`; produces `"title by @handle"` on X. |
54
+ | `nostr-hashtag` | `oksigenia` | Appended to the copied Nostr payload. |
55
+ | `no-label` | – | Boolean; if present, hides the `SHARE` text. |
56
+
57
+ CSS is encapsulated in the shadow DOM — no global stylesheet to load.
58
+
59
+ ## Use it imperatively (light DOM)
60
+
61
+ ```ts
62
+ import { mountShare } from '@oksigenia/share';
63
+ import '@oksigenia/share/styles.css'; // load the global styles once
64
+
65
+ const target = document.querySelector('#share-here')!;
66
+ const { destroy } = mountShare(target, {
67
+ title: 'Vori vori paraguayo',
68
+ url: 'https://granjaoga.com/articulos/vori-vori',
69
+ locale: 'es-PY',
70
+ networks: ['x', 'wa', 'tg', 'em'],
71
+ xHandle: 'granjaoga',
72
+ });
73
+
74
+ // later
75
+ destroy();
76
+ ```
77
+
78
+ ## Use it in Astro
79
+
80
+ ```astro
81
+ ---
82
+ const { url, title } = Astro.props;
83
+ ---
84
+ <script>
85
+ import '@oksigenia/share/web-component';
86
+ </script>
87
+ <oksigenia-share url={url} title={title} locale="es-PY"></oksigenia-share>
88
+ ```
89
+
90
+ ## Low-level building blocks
91
+
92
+ ```ts
93
+ import { buildShareLink, getTranslation, NETWORKS } from '@oksigenia/share';
94
+
95
+ const link = buildShareLink({
96
+ network: 'wa',
97
+ title: 'Mi receta',
98
+ url: 'https://example.com',
99
+ });
100
+
101
+ const t = getTranslation('es-PY');
102
+ // t.share === 'COMPARTIR'
103
+ ```
104
+
105
+ ## External services touched
106
+
107
+ The buttons redirect the user (and only on explicit click) to:
108
+
109
+ - twitter.com / x.com — [Privacy](https://twitter.com/privacy) · [Terms](https://twitter.com/tos)
110
+ - bsky.app — [Privacy](https://blueskyweb.xyz/support/privacy-policy) · [Terms](https://blueskyweb.xyz/support/tos)
111
+ - threads.net — [Privacy](https://help.instagram.com/519522125107875) · [Terms](https://help.instagram.com/581066165581870)
112
+ - whatsapp.com — [Privacy](https://www.whatsapp.com/legal/privacy-policy) · [Terms](https://www.whatsapp.com/legal/terms-of-service)
113
+ - telegram.org — [Privacy](https://telegram.org/privacy) · [Terms](https://telegram.org/tos)
114
+ - linkedin.com — [Privacy](https://www.linkedin.com/legal/privacy-policy) · [Terms](https://www.linkedin.com/legal/user-agreement)
115
+ - reddit.com — [Privacy](https://www.reddit.com/policies/privacy-policy) · [Terms](https://www.redditinc.com/policies/user-agreement)
116
+
117
+ Nostr copies a text payload to the clipboard (no network call).
118
+ Email uses the `mailto:` scheme (handled by the user agent).
119
+
120
+ ## License
121
+
122
+ [MIT](../../LICENSE) © [Oksigenia SL](https://oksigenia.com).
@@ -0,0 +1,104 @@
1
+ type NetworkId = 'x' | 'bs' | 'th' | 'wa' | 'tg' | 'li' | 'rd' | 'no' | 'em';
2
+ type OpenStrategy = 'popup' | 'tab' | 'copy' | 'email';
3
+ interface NetworkDef {
4
+ readonly id: NetworkId;
5
+ readonly label: string;
6
+ readonly color: string;
7
+ readonly open: OpenStrategy;
8
+ readonly svg: string;
9
+ }
10
+ declare const NETWORKS: Readonly<Record<NetworkId, NetworkDef>>;
11
+ declare const ALL_NETWORKS: readonly NetworkId[];
12
+
13
+ interface BuildLinkInput {
14
+ network: NetworkId;
15
+ title: string;
16
+ url: string;
17
+ /** Handle X opcional (sin @). Si está, se añade "{title} by @{handle}" al tweet. */
18
+ xHandle?: string;
19
+ /** Conector "by" según locale (i18n). Default "by". */
20
+ byWord?: string;
21
+ }
22
+ /**
23
+ * Construye la URL del share-intent de cada red. No abre nada, solo devuelve
24
+ * la cadena. Para Nostr (copy-only) devuelve cadena vacía — el contenido
25
+ * que se copia se construye con `buildNostrPayload()`.
26
+ */
27
+ declare function buildShareLink(input: BuildLinkInput): string;
28
+ /**
29
+ * Payload Nostr: el botón Nostr copia esto al portapapeles porque no hay
30
+ * un share-intent oficial. Hashtag configurable.
31
+ */
32
+ declare function buildNostrPayload(title: string, url: string, hashtag?: string): string;
33
+
34
+ type LocaleCode = 'en' | 'es' | 'it' | 'nl' | 'de' | 'pt' | 'gn' | 'sv';
35
+ interface Translation {
36
+ /** Label "SHARE" en caps. */
37
+ share: string;
38
+ /** Conector "by" usado en el tweet "{title} by @user". */
39
+ by: string;
40
+ /** aria-label "Share on %s". %s se reemplaza con el nombre de red. */
41
+ shareOn: (network: string) => string;
42
+ /** aria-label del botón de email. */
43
+ shareEmail: string;
44
+ /** aria-label del botón Nostr (copy-only). */
45
+ copyLink: string;
46
+ /** Anunciado por aria-live cuando se copia algo. */
47
+ copied: string;
48
+ /** Prompt fallback cuando clipboard no disponible. */
49
+ copyPrompt: string;
50
+ }
51
+ /**
52
+ * Devuelve la traducción para un locale. Acepta variantes regionales:
53
+ * `es-PY`, `es-ES`, `pt-BR` → todas caen al código base si existe.
54
+ * Fallback final: `en`.
55
+ */
56
+ declare function getTranslation(locale: string): Translation;
57
+ declare function supportedLocales(): readonly LocaleCode[];
58
+
59
+ interface ShareOptions {
60
+ /** URL absoluta a compartir. Si se omite, `location.href` al render. */
61
+ url?: string;
62
+ /** Título o texto del share. Si se omite, `document.title`. */
63
+ title?: string;
64
+ /** Locale (es, en, gn, …). Default: navegador, fallback en. */
65
+ locale?: LocaleCode | string;
66
+ /** Subconjunto/orden de redes. Default: las 9 en orden canónico. */
67
+ networks?: readonly NetworkId[];
68
+ /** Handle de X opcional (con o sin @). */
69
+ xHandle?: string;
70
+ /** Hashtag opcional para el payload Nostr. Default "oksigenia". */
71
+ nostrHashtag?: string;
72
+ /** Mostrar el texto "SHARE" a la izquierda. Default true. */
73
+ showLabel?: boolean;
74
+ }
75
+ /**
76
+ * Construye el markup HTML del panel de botones. NO lo monta en el DOM;
77
+ * el consumidor decide dónde inyectarlo y si quiere shadow DOM o no.
78
+ */
79
+ declare function buildShareHtml(opts?: ShareOptions): string;
80
+ /**
81
+ * Engancha los listeners de click a los botones renderizados dentro de
82
+ * `root`. Devuelve un dispose para limpiar.
83
+ */
84
+ declare function bindShareEvents(root: ParentNode, opts?: ShareOptions): () => void;
85
+
86
+ declare const SHARE_CSS = "\n:host { display: block; }\n.oksigenia-panel {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 30px 0;\n padding: 10px 18px;\n background: rgba(0,0,0,0.03);\n border-radius: 60px;\n width: fit-content;\n flex-wrap: wrap;\n font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n}\n.oksigenia-label {\n font-weight: 800;\n font-size: 11px;\n margin-right: 12px;\n text-transform: uppercase;\n color: #555;\n letter-spacing: 1.5px;\n}\n.oksigenia-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n border-radius: 50%;\n border: none;\n cursor: pointer;\n transition: transform .2s, filter .2s, box-shadow .2s;\n box-shadow: 0 3px 6px rgba(0,0,0,0.1);\n position: relative;\n text-decoration: none !important;\n padding: 0;\n overflow: visible;\n}\n.oksigenia-btn:hover {\n transform: translateY(-3px);\n filter: brightness(1.1);\n box-shadow: 0 5px 12px rgba(0,0,0,0.2);\n}\n.oksigenia-btn:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 3px;\n}\n.oksigenia-btn svg {\n width: 22px;\n height: 22px;\n fill: #fff;\n pointer-events: none;\n}\n.oksigenia-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0,0,0,0);\n white-space: nowrap;\n border: 0;\n}\n.o-no.copied { background: #00a884 !important; }\n@media (max-width: 480px) {\n .oksigenia-panel { padding: 8px 12px; gap: 8px; justify-content: center; }\n .oksigenia-btn { width: 38px; height: 38px; }\n .oksigenia-btn svg { width: 18px; height: 18px; }\n .oksigenia-label { margin-right: 6px; }\n}\n@media (prefers-reduced-motion: reduce) {\n .oksigenia-btn { transition: none; }\n .oksigenia-btn:hover { transform: none; }\n}\n";
87
+
88
+ interface MountResult {
89
+ /** Elemento .oksigenia-panel insertado. */
90
+ panel: HTMLElement;
91
+ /** Limpia listeners y remueve el panel del DOM. */
92
+ destroy: () => void;
93
+ }
94
+ /**
95
+ * Inserta el panel de botones dentro de `target` (light DOM) y engancha
96
+ * listeners. El CSS NO se inyecta — asegurate de cargar
97
+ * `@oksigenia/share/styles.css` o de inyectar `SHARE_CSS` por tu cuenta.
98
+ *
99
+ * Para inyección automática con shadow DOM aislado, usa el web component:
100
+ * `import '@oksigenia/share/web-component'` y `<oksigenia-share>`.
101
+ */
102
+ declare function mountShare(target: HTMLElement, options?: ShareOptions): MountResult;
103
+
104
+ export { ALL_NETWORKS, type BuildLinkInput, type LocaleCode, type MountResult, NETWORKS, type NetworkDef, type NetworkId, type OpenStrategy, SHARE_CSS, type ShareOptions, type Translation, bindShareEvents, buildNostrPayload, buildShareHtml, buildShareLink, getTranslation, mountShare, supportedLocales };
package/dist/index.js ADDED
@@ -0,0 +1,361 @@
1
+ // src/networks.ts
2
+ var X_SVG = '<svg viewBox="0 0 512 512" aria-hidden="true" focusable="false"><path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></svg>';
3
+ var BS_SVG = '<svg viewBox="0 0 600 530" aria-hidden="true" focusable="false"><path d="M135.7 44.4C202.6 94.7 274.6 196.7 301 251.4c26.4-54.7 98.4-156.7 165.3-207C512.7 9.5 587 -16.3 587 67.4c0 16.7-9.6 140.5-15.2 160.6-19.6 70-91 87.8-154.4 77 110.9 18.9 139.1 81.4 78.2 144-115.7 119-166.4-29.8-179.4-67.9-2.4-7-3.5-10.3-3.5-7.5 0 -2.8-1.1 .5-3.5 7.5-13 38.1-63.7 186.9-179.4 67.9-60.9-62.6-32.7-125.1 78.2-144-63.5 10.8-134.8-7.1-154.4-77C48 207.8 38.4 84.1 38.4 67.4 38.4-16.3 112.7 9.5 135.7 44.4z"/></svg>';
4
+ var TH_SVG = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.783 3.631 2.698 6.54 2.717 2.623-.02 4.358-.631 5.8-2.045 1.647-1.613 1.618-3.593 1.09-4.798-.31-.71-.873-1.3-1.634-1.75-.192 1.352-.622 2.446-1.284 3.272-.886 1.102-2.14 1.704-3.73 1.79-1.202.065-2.361-.218-3.259-.801-1.063-.689-1.685-1.74-1.752-2.964-.065-1.19.408-2.285 1.33-3.082.88-.76 2.119-1.207 3.583-1.291a13.853 13.853 0 0 1 3.02.142c-.126-.742-.375-1.332-.749-1.757-.513-.586-1.308-.883-2.359-.89h-.029c-.844 0-1.992.232-2.721 1.32L7.734 7.847c.98-1.454 2.568-2.256 4.478-2.256h.044c3.194.02 5.097 1.975 5.287 5.388.108.046.216.094.323.144 1.49.7 2.58 1.761 3.154 3.07.797 1.82.871 4.79-1.548 7.158-1.85 1.81-4.094 2.628-7.277 2.65zm1.003-11.69c-.242 0-.487.007-.739.021-1.836.103-2.98.946-2.916 2.143.067 1.256 1.452 1.839 2.784 1.767 1.224-.065 2.818-.543 3.086-3.71a10.5 10.5 0 0 0-2.215-.221z"/></svg>';
5
+ var WA_SVG = '<svg viewBox="0 0 448 512" aria-hidden="true" focusable="false"><path d="M380.9 97.1C339 55.1 283.2 32 223.9 32c-122.4 0-222 99.6-222 222 0 39.1 10.2 77.3 29.6 111L0 480l117.7-30.9c32.4 17.7 68.9 27 106.1 27h.1c122.3 0 224.1-99.6 224.1-222 0-59.3-25.2-115-67.1-157zm-157 341.6c-33.2 0-65.7-8.9-94-25.7l-6.7-4-69.8 18.3L72 359.2l-4.4-7c-18.5-29.4-28.2-63.3-28.2-98.2 0-101.7 82.8-184.5 184.6-184.5 49.3 0 95.6 19.2 130.4 54.1 34.8 34.9 56.2 81.2 56.1 130.5 0 101.8-84.9 184.6-186.6 184.6zm101.2-138.2c-5.5-2.8-32.8-16.2-37.9-18-5.1-1.9-8.8-2.8-12.5 2.8-3.7 5.6-14.3 18-17.6 21.8-3.2 3.7-6.5 4.2-12 1.4-5.5-2.8-23.4-8.6-44.6-27.5-16.5-14.7-27.6-32.8-30.8-38.4-3.2-5.6-.3-8.6 2.5-11.4 2.5-2.5 5.5-6.5 8.3-9.7 2.8-3.2 3.7-5.5 5.5-9.2 1.9-3.7.9-6.9-.5-9.7-1.4-2.8-12.5-30.1-17.1-41.2-4.5-10.8-9.1-9.3-12.5-9.5-3.2-.2-6.9-.2-10.6-.2-3.7 0-9.7 1.4-14.8 6.9-5.1 5.6-19.4 19-19.4 46.3 0 27.3 19.9 53.7 22.6 57.4 2.8 3.7 39.1 59.7 94.8 83.8 13.2 5.8 23.5 9.2 31.5 11.8 13.3 4.2 25.4 3.6 35 2.2 10.7-1.6 32.8-13.4 37.4-26.4 4.6-13 4.6-24.1 3.2-26.4-1.3-2.5-5-3.9-10.5-6.6z"/></svg>';
6
+ var TG_SVG = '<svg viewBox="0 0 448 512" aria-hidden="true" focusable="false"><path d="M446.7 98.6l-67.6 318.8c-5.1 22.5-18.4 28.1-37.3 17.5l-103-75.9-49.7 47.8c-5.5 5.5-10.1 10.1-20.7 10.1l7.4-104.9 190.9-172.5c8.3-7.4-1.8-11.5-12.9-4.1L117.8 284 16.2 252.2c-22.1-6.9-22.5-22.1 4.6-32.7L418.2 66.4c18.4-6.9 34.5 4.1 28.5 32.2z"/></svg>';
7
+ var LI_SVG = '<svg viewBox="0 0 448 512" aria-hidden="true" focusable="false"><path d="M100.28 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.58 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z"/></svg>';
8
+ var RD_SVG = '<svg viewBox="0 0 512 512" aria-hidden="true" focusable="false"><path d="M201.5 305.5c-13.8 0-24.9-11.1-24.9-24.6 0-13.8 11.1-24.9 24.9-24.9 13.6 0 24.6 11.1 24.6 24.9 0 13.6-11.1 24.6-24.6 24.6zm129 41.2c-12.3 12.3-37.8 13.4-45.3 13.4-7.5 0-32.7-1.4-45-13.4-1.8-1.8-1.8-4.8 0-6.9 1.8-1.8 4.8-1.8 6.6 0 7.8 7.8 24.3 10.5 38.4 10.5s30.9-2.7 38.7-10.5c1.8-1.8 4.8-1.8 6.6 0 1.8 2.1 1.8 5.1 0 6.9zm-12.3-41.2c0-13.8 11.1-24.9 24.9-24.9 13.6 0 24.6 11.1 24.6 24.9 0 13.6-11.1 24.6-24.6 24.6-13.8 0-24.9-11.1-24.9-24.6zM504 256c0 137-111 248-248 248S8 393 8 256 119 8 256 8s248 111 248 248zm-132.3-41.2c-9.4 0-17.7 3.9-23.8 9.9-22.8-15.6-52.8-25.8-86.1-27l17.4-78.9 55.5 12.3c0 13.6 11.1 24.6 24.6 24.6 13.8 0 24.9-11.4 24.9-24.9s-11.1-24.9-24.9-24.9c-9.6 0-17.7 5.7-21.6 13.5l-61.5-13.5c-3-.9-6 1.2-6.6 4.2l-19.2 86.7c-32.7 1.5-62.7 11.7-85.5 27.3-6-6.3-14.7-9.9-23.7-9.9-34.8 0-46.2 46.8-14.4 62.7-1.2 5.1-1.8 10.8-1.8 16.2 0 49.5 56.1 89.7 125.4 89.7 69.6 0 125.7-40.2 125.7-89.7 0-5.4-.6-11.4-2.1-16.5 31.5-15.9 19.8-62.4-14.4-62.4z"/></svg>';
9
+ var NO_SVG = '<svg viewBox="0 0 170.66667 170" aria-hidden="true" focusable="false"><path d="M 80.014749,0.78606659 C 73.052769,1.9302826 66.167099,2.3642936 59.348089,4.5268096 38.835519,11.031957 21.050593,25.529048 10.848085,44.52681 -6.8624293,77.505 -0.92508032,121.67898 27.348086,147.13792 c 11.539173,10.3906 24.998793,17.01823 40.000003,20.62963 9.26968,2.23159 19.24599,2.5862 28.66667,1.27777 C 139.11925,163.05859 171.85692,124.19929 169.32958,80.52681 167.31241,45.669937 143.27583,13.848984 109.34809,4.2675526 100.31238,1.7158046 89.426889,-0.76084941 80.014749,0.78606659 M 136.01476,34.52681 c -2.37396,0 -11.65111,-0.914032 -11.03704,3.333333 0.44515,3.078919 4.97786,5.419108 7.01853,7.388886 2.78243,2.685771 4.44989,6.742004 4.6111,10.611114 0.43316,10.395773 -8.18644,14.669088 -14.53704,20.81482 -4.11595,3.983174 -1.16762,10.854736 -6.07406,15.018514 -5.28385,4.484059 -13.33279,3.273844 -19.314831,6.111114 -4.69327,2.226009 -7.84703,7.913129 -10.66667,12.055549 l 14.666681,-4.44443 7.33333,-1.22223 5.98149,6.33333 2.01851,5.33333 c -2.72032,-0.0572 -6.29832,-0.90982 -8.61111,-2.42592 -1.43022,-0.93754 -2.13911,-2.61031 -4.05556,-2.55556 -1.73865,0.0497 -3.687801,1.1301 -5.333331,1.64815 -4.89415,1.54075 -9.77338,3.60264 -14.66667,5.01851 -4.97774,1.44033 -6.14184,-3.22559 -4.5,-7.01851 0.58509,-1.35166 1.3007,-2.70345 2,-4 1.22817,-2.27712 2.8155,-4.29175 3.83333,-6.666667 -14.44897,7.042367 -23.21779,20.075037 -32.57408,32.666667 -2.69666,3.62915 -5.83349,7.00277 -8.44444,10.66667 -0.74469,1.045 -1.43956,3.39244 -3.01852,3.44444 -1.48153,0.0488 -1.89431,-1.62287 -1.88889,-2.77778 0.012,-2.56211 1.55286,-4.83939 1.87038,-7.33333 0.16984,-1.33409 -0.3317,-3.17503 0.53703,-4.33333 1.46427,-1.95236 4.81923,-1.69841 6.5926,-3.68518 4.11706,-4.61253 7.99675,-10.65312 11.12963,-15.98149 1.00019,-1.70113 1.31074,-3.7756 2.62962,-5.27777 4.15881,-4.73677 10.29882,-7.15127 13.83334,-12.72223 -3.78599,-0.77946 -7.81198,-1.877889 -11.33334,-3.5 -2.16788,-0.998637 -4.28318,-2.695353 -6.66666,-3.09259 -4.92365,-0.820608 -7.70844,2.301147 -12,3.59259 -3.55609,1.070119 -9.1464,-2.835714 -11,-5.666667 -2.9074,-4.440389 1.59973,-11.14448 5.66666,-13.055562 7.85432,-3.690796 17.10949,-1.957876 25.33334,-4.185181 8.80831,-2.385589 15.88491,-8.429891 25.33333,-9.370371 6.13412,-0.610576 12.248691,0.007 18.000011,2.203705 5.40278,2.063568 12.35279,5.71113 18,2.462972 3.05718,-1.758433 5.38171,-6.552399 4.35184,-10.055563 -1.22555,-4.168813 -5.35998,-6.611246 -8.35184,-9.388885 -3.4549,-3.20752 -5.13367,-7.241526 -4.61112,-11.944448 0.93488,-8.413696 8.07782,-5.219116 12.61112,-2.333333 2.37224,1.510111 4.74974,1.248728 5.33333,4.333333 z"/></svg>';
10
+ var EM_SVG = '<svg viewBox="0 0 512 512" aria-hidden="true" focusable="false"><path d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z"/></svg>';
11
+ var NETWORKS = {
12
+ x: { id: "x", label: "X (Twitter)", color: "#000000", open: "popup", svg: X_SVG },
13
+ bs: { id: "bs", label: "Bluesky", color: "#0285FF", open: "tab", svg: BS_SVG },
14
+ th: { id: "th", label: "Threads", color: "#000000", open: "tab", svg: TH_SVG },
15
+ wa: { id: "wa", label: "WhatsApp", color: "#25D366", open: "tab", svg: WA_SVG },
16
+ tg: { id: "tg", label: "Telegram", color: "#0088cc", open: "tab", svg: TG_SVG },
17
+ li: { id: "li", label: "LinkedIn", color: "#0077b5", open: "popup", svg: LI_SVG },
18
+ rd: { id: "rd", label: "Reddit", color: "#FF4500", open: "popup", svg: RD_SVG },
19
+ no: { id: "no", label: "Nostr", color: "#8e44ad", open: "copy", svg: NO_SVG },
20
+ em: { id: "em", label: "Email", color: "#666666", open: "email", svg: EM_SVG }
21
+ };
22
+ var ALL_NETWORKS = [
23
+ "x",
24
+ "bs",
25
+ "th",
26
+ "wa",
27
+ "tg",
28
+ "li",
29
+ "rd",
30
+ "no",
31
+ "em"
32
+ ];
33
+
34
+ // src/build-link.ts
35
+ function buildShareLink(input) {
36
+ const { network, title, url, xHandle, byWord = "by" } = input;
37
+ const txt = encodeURIComponent(title);
38
+ const urlEnc = encodeURIComponent(url);
39
+ const xText = xHandle ? encodeURIComponent(`${title} ${byWord} @${xHandle.replace(/^@/, "")}`) : txt;
40
+ switch (network) {
41
+ case "x":
42
+ return `https://twitter.com/intent/tweet?text=${xText}&url=${urlEnc}`;
43
+ case "bs":
44
+ return `https://bsky.app/intent/compose?text=${encodeURIComponent(`${title} ${url}`)}`;
45
+ case "th":
46
+ return `https://www.threads.net/intent/post?text=${encodeURIComponent(`${title} ${url}`)}`;
47
+ case "wa":
48
+ return `https://api.whatsapp.com/send?text=${txt}%20${urlEnc}`;
49
+ case "tg":
50
+ return `https://t.me/share/url?url=${urlEnc}&text=${txt}`;
51
+ case "li":
52
+ return `https://www.linkedin.com/sharing/share-offsite/?url=${urlEnc}`;
53
+ case "rd":
54
+ return `https://www.reddit.com/submit?url=${urlEnc}&title=${txt}`;
55
+ case "em":
56
+ return `mailto:?subject=${txt}&body=${urlEnc}`;
57
+ case "no":
58
+ return "";
59
+ }
60
+ }
61
+ function buildNostrPayload(title, url, hashtag = "oksigenia") {
62
+ return `${title}
63
+ ${url}
64
+ #${hashtag}`;
65
+ }
66
+
67
+ // src/translations.ts
68
+ var DICT = {
69
+ en: {
70
+ share: "SHARE",
71
+ by: "by",
72
+ shareOn: (n) => `Share on ${n}`,
73
+ shareEmail: "Share by email",
74
+ copyLink: "Copy link to clipboard",
75
+ copied: "Copied to clipboard",
76
+ copyPrompt: "Copy this:"
77
+ },
78
+ es: {
79
+ share: "COMPARTIR",
80
+ by: "por",
81
+ shareOn: (n) => `Compartir en ${n}`,
82
+ shareEmail: "Compartir por email",
83
+ copyLink: "Copiar enlace al portapapeles",
84
+ copied: "Copiado al portapapeles",
85
+ copyPrompt: "Copia esto:"
86
+ },
87
+ it: {
88
+ share: "CONDIVIDI",
89
+ by: "da",
90
+ shareOn: (n) => `Condividi su ${n}`,
91
+ shareEmail: "Condividi via email",
92
+ copyLink: "Copia il link negli appunti",
93
+ copied: "Copiato negli appunti",
94
+ copyPrompt: "Copia questo:"
95
+ },
96
+ nl: {
97
+ share: "DELEN",
98
+ by: "door",
99
+ shareOn: (n) => `Deel op ${n}`,
100
+ shareEmail: "Per e-mail delen",
101
+ copyLink: "Link kopi\xEBren naar klembord",
102
+ copied: "Gekopieerd naar klembord",
103
+ copyPrompt: "Kopieer dit:"
104
+ },
105
+ de: {
106
+ share: "TEILEN",
107
+ by: "von",
108
+ shareOn: (n) => `Auf ${n} teilen`,
109
+ shareEmail: "Per E-Mail teilen",
110
+ copyLink: "Link in die Zwischenablage kopieren",
111
+ copied: "In die Zwischenablage kopiert",
112
+ copyPrompt: "Dies kopieren:"
113
+ },
114
+ pt: {
115
+ share: "PARTILHAR",
116
+ by: "por",
117
+ shareOn: (n) => `Partilhar no ${n}`,
118
+ shareEmail: "Partilhar por email",
119
+ copyLink: "Copiar liga\xE7\xE3o para a \xE1rea de transfer\xEAncia",
120
+ copied: "Copiado para a \xE1rea de transfer\xEAncia",
121
+ copyPrompt: "Copiar isto:"
122
+ },
123
+ gn: {
124
+ share: "MOAS\xC3I",
125
+ by: "por",
126
+ shareOn: (n) => `Emoas\xE3i ${n}-pe`,
127
+ shareEmail: "Emoas\xE3i email rupive",
128
+ copyLink: "Ekopia link portapapeles-pe",
129
+ copied: "O\xF1ekopia portapapeles-pe",
130
+ copyPrompt: "Ekopia k\xF3va:"
131
+ },
132
+ sv: {
133
+ share: "DELA",
134
+ by: "av",
135
+ shareOn: (n) => `Dela p\xE5 ${n}`,
136
+ shareEmail: "Dela via e-post",
137
+ copyLink: "Kopiera l\xE4nk till urklipp",
138
+ copied: "Kopierat till urklipp",
139
+ copyPrompt: "Kopiera detta:"
140
+ }
141
+ };
142
+ function getTranslation(locale) {
143
+ const base = locale.toLowerCase().split(/[-_]/)[0];
144
+ return DICT[base] ?? DICT.en;
145
+ }
146
+ function supportedLocales() {
147
+ return Object.keys(DICT);
148
+ }
149
+
150
+ // src/render.ts
151
+ function prepareButtons(opts, t) {
152
+ const list = opts.networks ?? ALL_NETWORKS;
153
+ const out = [];
154
+ for (const id of list) {
155
+ const def = NETWORKS[id];
156
+ if (!def) continue;
157
+ const label = id === "em" ? t.shareEmail : id === "no" ? t.copyLink : t.shareOn(def.label);
158
+ const link = buildShareLink({
159
+ network: id,
160
+ title: opts.title,
161
+ url: opts.url,
162
+ xHandle: opts.xHandle,
163
+ byWord: t.by
164
+ });
165
+ const button = {
166
+ id,
167
+ ariaLabel: label,
168
+ type: def.open,
169
+ link,
170
+ bgColor: def.color,
171
+ svg: def.svg
172
+ };
173
+ if (id === "no") {
174
+ button.copyPayload = buildNostrPayload(opts.title, opts.url, opts.nostrHashtag);
175
+ }
176
+ out.push(button);
177
+ }
178
+ return out;
179
+ }
180
+ function buildShareHtml(opts = {}) {
181
+ const resolved = {
182
+ title: opts.title ?? (typeof document !== "undefined" ? document.title : ""),
183
+ url: opts.url ?? (typeof location !== "undefined" ? location.href : ""),
184
+ locale: opts.locale ?? (typeof navigator !== "undefined" ? navigator.language : "en"),
185
+ showLabel: opts.showLabel ?? true,
186
+ networks: opts.networks,
187
+ xHandle: opts.xHandle,
188
+ nostrHashtag: opts.nostrHashtag
189
+ };
190
+ const t = getTranslation(resolved.locale);
191
+ const buttons = prepareButtons(resolved, t);
192
+ const labelHtml = resolved.showLabel ? `<span class="oksigenia-label" aria-hidden="true">${escapeHtml(t.share)}</span>` : "";
193
+ const buttonsHtml = buttons.map((b) => {
194
+ const dataLink = b.link ? ` data-link="${escapeAttr(b.link)}"` : "";
195
+ const dataCopy = b.copyPayload ? ` data-copy="${escapeAttr(b.copyPayload)}"` : "";
196
+ return `<button type="button" class="oksigenia-btn o-${b.id}" style="background:${b.bgColor}" data-type="${b.type}"${dataLink}${dataCopy} aria-label="${escapeAttr(b.ariaLabel)}">${b.svg}<span class="oksigenia-sr-only" aria-live="polite"></span></button>`;
197
+ }).join("");
198
+ return `<div class="oksigenia-panel" role="group" aria-label="${escapeAttr(t.share)}">${labelHtml}${buttonsHtml}</div>`;
199
+ }
200
+ function escapeHtml(s) {
201
+ return s.replace(/[&<>"]/g, (c) => ({
202
+ "&": "&amp;",
203
+ "<": "&lt;",
204
+ ">": "&gt;",
205
+ '"': "&quot;"
206
+ })[c] ?? c);
207
+ }
208
+ function escapeAttr(s) {
209
+ return escapeHtml(s).replace(/'/g, "&#39;");
210
+ }
211
+ function bindShareEvents(root, opts = {}) {
212
+ const locale = opts.locale ?? (typeof navigator !== "undefined" ? navigator.language : "en");
213
+ const t = getTranslation(locale);
214
+ const buttons = Array.from(root.querySelectorAll(".oksigenia-btn"));
215
+ const handler = (e) => {
216
+ e.preventDefault();
217
+ const btn = e.currentTarget;
218
+ openShare(btn, t);
219
+ };
220
+ for (const b of buttons) b.addEventListener("click", handler);
221
+ return () => {
222
+ for (const b of buttons) b.removeEventListener("click", handler);
223
+ };
224
+ }
225
+ function openShare(btn, t) {
226
+ const link = btn.getAttribute("data-link") ?? "";
227
+ const type = btn.getAttribute("data-type") ?? "tab";
228
+ const live = btn.querySelector(".oksigenia-sr-only");
229
+ if (type === "popup") {
230
+ const w = 600;
231
+ const h = 400;
232
+ const left = (window.innerWidth - w) / 2;
233
+ const top = (window.innerHeight - h) / 2;
234
+ window.open(link, "oksigenia_share", `width=${w},height=${h},top=${top},left=${left},scrollbars=no`);
235
+ return;
236
+ }
237
+ if (type === "tab") {
238
+ window.open(link, "_blank", "noopener");
239
+ return;
240
+ }
241
+ if (type === "email") {
242
+ window.location.href = link;
243
+ return;
244
+ }
245
+ if (type === "copy") {
246
+ const text = btn.getAttribute("data-copy") ?? "";
247
+ const done = () => {
248
+ btn.classList.add("copied");
249
+ if (live) live.textContent = t.copied;
250
+ window.setTimeout(() => {
251
+ btn.classList.remove("copied");
252
+ if (live) live.textContent = "";
253
+ }, 2e3);
254
+ };
255
+ if (navigator.clipboard?.writeText) {
256
+ navigator.clipboard.writeText(text).then(done).catch(() => {
257
+ window.prompt(t.copyPrompt, text);
258
+ });
259
+ } else {
260
+ window.prompt(t.copyPrompt, text);
261
+ }
262
+ }
263
+ }
264
+
265
+ // src/styles.ts
266
+ var SHARE_CSS = `
267
+ :host { display: block; }
268
+ .oksigenia-panel {
269
+ display: flex;
270
+ align-items: center;
271
+ gap: 10px;
272
+ margin: 30px 0;
273
+ padding: 10px 18px;
274
+ background: rgba(0,0,0,0.03);
275
+ border-radius: 60px;
276
+ width: fit-content;
277
+ flex-wrap: wrap;
278
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
279
+ }
280
+ .oksigenia-label {
281
+ font-weight: 800;
282
+ font-size: 11px;
283
+ margin-right: 12px;
284
+ text-transform: uppercase;
285
+ color: #555;
286
+ letter-spacing: 1.5px;
287
+ }
288
+ .oksigenia-btn {
289
+ display: inline-flex;
290
+ align-items: center;
291
+ justify-content: center;
292
+ width: 44px;
293
+ height: 44px;
294
+ border-radius: 50%;
295
+ border: none;
296
+ cursor: pointer;
297
+ transition: transform .2s, filter .2s, box-shadow .2s;
298
+ box-shadow: 0 3px 6px rgba(0,0,0,0.1);
299
+ position: relative;
300
+ text-decoration: none !important;
301
+ padding: 0;
302
+ overflow: visible;
303
+ }
304
+ .oksigenia-btn:hover {
305
+ transform: translateY(-3px);
306
+ filter: brightness(1.1);
307
+ box-shadow: 0 5px 12px rgba(0,0,0,0.2);
308
+ }
309
+ .oksigenia-btn:focus-visible {
310
+ outline: 2px solid currentColor;
311
+ outline-offset: 3px;
312
+ }
313
+ .oksigenia-btn svg {
314
+ width: 22px;
315
+ height: 22px;
316
+ fill: #fff;
317
+ pointer-events: none;
318
+ }
319
+ .oksigenia-sr-only {
320
+ position: absolute;
321
+ width: 1px;
322
+ height: 1px;
323
+ padding: 0;
324
+ margin: -1px;
325
+ overflow: hidden;
326
+ clip: rect(0,0,0,0);
327
+ white-space: nowrap;
328
+ border: 0;
329
+ }
330
+ .o-no.copied { background: #00a884 !important; }
331
+ @media (max-width: 480px) {
332
+ .oksigenia-panel { padding: 8px 12px; gap: 8px; justify-content: center; }
333
+ .oksigenia-btn { width: 38px; height: 38px; }
334
+ .oksigenia-btn svg { width: 18px; height: 18px; }
335
+ .oksigenia-label { margin-right: 6px; }
336
+ }
337
+ @media (prefers-reduced-motion: reduce) {
338
+ .oksigenia-btn { transition: none; }
339
+ .oksigenia-btn:hover { transform: none; }
340
+ }
341
+ `;
342
+
343
+ // src/index.ts
344
+ function mountShare(target, options = {}) {
345
+ const wrapper = document.createElement("div");
346
+ wrapper.innerHTML = buildShareHtml(options);
347
+ const panel = wrapper.firstElementChild;
348
+ target.appendChild(panel);
349
+ const dispose = bindShareEvents(panel, options);
350
+ return {
351
+ panel,
352
+ destroy: () => {
353
+ dispose();
354
+ panel.remove();
355
+ }
356
+ };
357
+ }
358
+
359
+ export { ALL_NETWORKS, NETWORKS, SHARE_CSS, bindShareEvents, buildNostrPayload, buildShareHtml, buildShareLink, getTranslation, mountShare, supportedLocales };
360
+ //# sourceMappingURL=index.js.map
361
+ //# sourceMappingURL=index.js.map