@meoslabs/save-in-meos 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 meoslabs
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.
22
+
23
+ ---
24
+
25
+ Bundled font files under assets/fonts/ are licensed separately under the
26
+ SIL Open Font License 1.1 — see assets/fonts/inconsolata/OFL.txt.
package/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # @meoslabs/save-in-meos
2
+
3
+ **meos deeplink protocol (MDP)** codec and **save in meos** embed widget for third-party sites.
4
+
5
+ Let visitors save a page (or a quote from it) into [meos](https://meos.do) with one tap. This package builds canonical import URLs and ships a branded, self-contained button you can drop into any site — via npm, a script tag, or programmatic imports only.
6
+
7
+ ## How to integrate
8
+
9
+ | Use case | How |
10
+ |----------|-----|
11
+ | **npm / bundler** | `npm install @meoslabs/save-in-meos` then `import { initSaveButton } from '@meoslabs/save-in-meos'` |
12
+ | **Script tag (CDN)** | Pin `https://unpkg.com/@meoslabs/save-in-meos@VERSION/dist/widget.iife.js` + `fonts.css` — see [`examples/cdn-demo.html`](examples/cdn-demo.html) |
13
+ | **Programmatic only** | `import { buildMeosLink, buildImportIntentV1 } from '@meoslabs/save-in-meos'` — no widget CSS required |
14
+ | **Local demo** | `npm run demo` then open `http://localhost:4173/demo?local=1` (stages `examples/vendor/` via `build:widget`) |
15
+
16
+ CDN mirrors (auto-indexed from npm — no separate account):
17
+
18
+ - **unpkg:** `https://unpkg.com/@meoslabs/save-in-meos@VERSION/dist/widget.iife.js`
19
+ - **jsDelivr:** `https://cdn.jsdelivr.net/npm/@meoslabs/save-in-meos@VERSION/dist/widget.iife.js`
20
+
21
+ Alias: `dist/save-in-meos.min.js` (identical minified IIFE).
22
+
23
+ ## Widget appearance
24
+
25
+ The **save in meos** chip uses a **closed shadow root**. Integrators cannot change the label, font (Inconsolata), or logo SVG path. The logo is **vector** (from `assets/branding/meos-logo-charcoal-nostroke.svg`), rendered at **16px mark height** by default with the correct brand aspect ratio.
26
+
27
+ | Customisable | Fixed (brand) |
28
+ |--------------|---------------|
29
+ | `theme: "auto" \| "light" \| "dark"` | Font family + weight |
30
+ | `chipPreset: "default" \| "compact"` | Logo SVG path |
31
+ | Chip height (28–40px), padding, radius | Arbitrary label text |
32
+ | Logo mark height (11–16px) | Logo animation (static mark only) |
33
+
34
+ ### Chip presets
35
+
36
+ Two presets only — pick size and label together:
37
+
38
+ | Preset | Visible label | Use when | Height | Padding X | Radius |
39
+ |--------|---------------|----------|--------|-----------|--------|
40
+ | `default` | **save in meos** | Share rows (default) | 31px | 10px | 2px |
41
+ | `compact` | **save** | Dense toolbars | 28px | 8px | 2px |
42
+
43
+ `aria-label` is always **save in meos** (accessibility). Only the visible chip text shortens on `compact`.
44
+
45
+ ```ts
46
+ initSaveButton("#meos-save-mount", {
47
+ u: location.href,
48
+ widgetId: "my-site",
49
+ theme: "dark",
50
+ chipPreset: "compact",
51
+ })
52
+ ```
53
+
54
+ Explicit `chip` fields override preset values. Prefer `chipPreset` over hand-rolled pixel values.
55
+
56
+ **Theme:** `auto` follows OS `prefers-color-scheme`. Pin `light` or `dark` when your page theme differs from the OS.
57
+
58
+ **Advanced shape via CSS on the mount host:**
59
+
60
+ ```css
61
+ #meos-save-mount {
62
+ --meos-save-chip-height: 36px;
63
+ --meos-save-icon-size: 16px;
64
+ }
65
+ ```
66
+
67
+ **Live examples** (after `npm run demo`):
68
+
69
+ | Demo | URL |
70
+ |------|-----|
71
+ | Blog post embed | `http://localhost:4173/demo.html?local=1` |
72
+ | Theme + chip presets | `http://localhost:4173/theme-demo.html` |
73
+ | CDN copy/paste snippet | `http://localhost:4173/cdn-demo.html` |
74
+
75
+ See [`docs/INTEGRATOR.md`](docs/INTEGRATOR.md) for the full widget API.
76
+
77
+ ## Quick start — npm widget
78
+
79
+ ```ts
80
+ import "@meoslabs/save-in-meos/fonts.css"
81
+ import "@meoslabs/save-in-meos/widget.css"
82
+ import { initSaveButton } from "@meoslabs/save-in-meos"
83
+
84
+ initSaveButton("#meos-save-mount", {
85
+ u: "https://example.com/article",
86
+ widgetId: "my-site",
87
+ })
88
+ ```
89
+
90
+ ## Quick start — script tag
91
+
92
+ ```html
93
+ <link rel="stylesheet" href="https://unpkg.com/@meoslabs/save-in-meos@0.0.1/src/widget/fonts.css" />
94
+ <div id="meos-save-mount"></div>
95
+ <script src="https://unpkg.com/@meoslabs/save-in-meos@0.0.1/dist/widget.iife.js"></script>
96
+ <script>
97
+ MeosSave.initSaveButton("#meos-save-mount", {
98
+ u: location.href,
99
+ widgetId: "my-site",
100
+ })
101
+ </script>
102
+ ```
103
+
104
+ ## Quick start — build links programmatically
105
+
106
+ ```ts
107
+ import {
108
+ buildMeosLink,
109
+ buildImportIntentV1,
110
+ decodeMeosLink,
111
+ type ImportIntentV1,
112
+ } from "@meoslabs/save-in-meos"
113
+
114
+ const intent = buildImportIntentV1({
115
+ u: "https://example.com/article",
116
+ t: "Optional selected quote",
117
+ })
118
+
119
+ const url = buildMeosLink(intent, "my-widget")
120
+ const roundtrip = decodeMeosLink(url)
121
+ ```
122
+
123
+ See [`docs/INTEGRATOR.md`](docs/INTEGRATOR.md) for tiers, branding rules, and Universal Links.
124
+
125
+ ## What is MDP?
126
+
127
+ The **meos deeplink protocol** encodes an import intent — URL, optional quoted text, images — into a compact `https://meos.do/databox:import:…` link. Widget attribution travels in the `?w=` query param.
128
+
129
+ | Tier | Use when |
130
+ |------|----------|
131
+ | **REF** | Page URL only |
132
+ | **LITE** | URL + selected quote text |
133
+ | **IMG** | URL + image URLs |
134
+ | **FULL** | URL + structured blocks (advanced) |
135
+
136
+ ## Development
137
+
138
+ ```bash
139
+ npm install
140
+ npm run build
141
+ npm run build:widget # dist/widget.iife.js for CDN / script tag
142
+ npm run demo # build + serve examples (local widget demos)
143
+ npm test
144
+ npm run check:mdp # contract + branding gates
145
+ npm run check:public-scrub # no internal paths / secrets in docs
146
+ npm run check:ci # GitHub Actions workflow ratchet
147
+ ```
148
+
149
+ ## Publishing
150
+
151
+ See [`docs/PUBLISHING.md`](docs/PUBLISHING.md) for the full checklist. Summary:
152
+
153
+ 1. **npm scope** — publish as `@meoslabs/save-in-meos` (or `@meos/…` if the org scope is available on npmjs.com)
154
+ 2. **First publish** — `npm login` → `npm publish --access public` (or GitHub Release → OIDC trusted publishing)
155
+ 3. **CDN** — unpkg/jsDelivr index the npm tarball automatically; pin `VERSION` in integrator HTML
156
+ 4. **Optional** — `meo cdn put dist/widget.iife.js` only if you also want a copy on `static.usemeos.com` (separate from npm mirrors)
157
+
158
+ ## Docs
159
+
160
+ | Doc | Audience |
161
+ |-----|----------|
162
+ | [`docs/INTEGRATOR.md`](docs/INTEGRATOR.md) | Site owners embedding the widget |
163
+ | [`docs/PUBLISHING.md`](docs/PUBLISHING.md) | Maintainers — npm publish + CI secrets |
164
+ | [`docs/QA-MDP-SMOKE.md`](docs/QA-MDP-SMOKE.md) | Smoke-test this package before release |
165
+ | [`docs/RELEASE-MDP-DEEPLINKS.md`](docs/RELEASE-MDP-DEEPLINKS.md) | App Links / Universal Links for integrators |
166
+ | [`CONTRIBUTING.md`](CONTRIBUTING.md) | meoslabs contributors |
167
+
168
+ ## Licence
169
+
170
+ - **Code:** [MIT](LICENSE)
171
+ - **Fonts:** Inconsolata [OFL-1.1](assets/fonts/inconsolata/OFL.txt) (bundled in the package)
@@ -0,0 +1,93 @@
1
+ Copyright 2006 The Inconsolata Project Authors
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,88 @@
1
+ /**
2
+ * WHY: ImportIntentV1 is the codec SSOT for meos deeplink protocol (MDP).
3
+ * Encode/decode for databox:import must live only in this package.
4
+ * WHAT: Types + encode/decode/buildMeosLink for `databox:import` URLs.
5
+ * HOW: Optimised wire schema (k: u|ut|i|f) → JSON → deflateRaw → base64url.
6
+ * WHERE: @meoslabs/save-in-meos — consumed by widget and meos clients.
7
+ * GUARDED: check-mdp-contract.ts golden fixtures in fixtures/mdp/.
8
+ */
9
+ /** MDP contract version — semver pinned by golden fixture checkers. */
10
+ export declare const MDP_CONTRACT_VERSION = "0.0.1";
11
+ export type ImportIntentTier = "REF" | "LITE" | "IMG" | "FULL";
12
+ /** Wire kind — compact tier discriminator inside the compressed blob. */
13
+ export type ImportIntentKind = "u" | "ut" | "i" | "f";
14
+ /** Canonical meos.do host for import deeplinks. */
15
+ export declare const MEOS_DO_HOST = "meos.do";
16
+ /** Colon-grammar resource for widget import (not facility bulk import). */
17
+ export declare const DATABOX_IMPORT_RESOURCE = "databox:import";
18
+ /** Maximum URL length before QR degradation (bytes, conservative for Level-M QR). */
19
+ export declare const MDP_MAX_QR_URL_LENGTH = 2048;
20
+ export interface ImportIntentV1 {
21
+ /** Schema version — always 1 for v1 codec. */
22
+ v: 1;
23
+ tier: ImportIntentTier;
24
+ /** Canonical content URL (REF/LITE/IMG/FULL). */
25
+ u: string;
26
+ /** Optional quoted text (LITE tier). */
27
+ t?: string;
28
+ /** Optional image URLs (IMG tier). */
29
+ images?: string[];
30
+ /**
31
+ * Widget attribution id — expressible via ?w= query param only.
32
+ * Never encoded into the compressed blob.
33
+ */
34
+ w?: string;
35
+ /** Optional PadSeed-shaped blocks (FULL tier). */
36
+ blocks?: unknown[];
37
+ }
38
+ /** Input for automatic tier selection before building an intent. */
39
+ export interface ImportIntentInput {
40
+ u: string;
41
+ t?: string;
42
+ images?: string[];
43
+ blocks?: unknown[];
44
+ }
45
+ export declare class MdpEncodeError extends Error {
46
+ readonly cause?: unknown | undefined;
47
+ constructor(message: string, cause?: unknown | undefined);
48
+ }
49
+ export declare class MdpDecodeError extends Error {
50
+ readonly cause?: unknown | undefined;
51
+ constructor(message: string, cause?: unknown | undefined);
52
+ }
53
+ /**
54
+ * Select the minimum encoding tier for the given content.
55
+ * REF — URL only; LITE — URL + distinct text; IMG — image URLs; FULL — blocks.
56
+ */
57
+ export declare function selectImportTier(input: ImportIntentInput): ImportIntentTier;
58
+ /** Build a typed ImportIntentV1 from loose widget input. */
59
+ export declare function buildImportIntentV1(input: ImportIntentInput): ImportIntentV1;
60
+ /**
61
+ * Encode ImportIntentV1 to a URL-safe payload segment (no host/path).
62
+ * Widget attribution (?w=) is never included in the blob.
63
+ */
64
+ export declare function encodeImportIntentV1(intent: ImportIntentV1): string;
65
+ /**
66
+ * Decode a databox:import payload segment back to ImportIntentV1.
67
+ * Does not parse ?w= — use decodeMeosLink for full URLs.
68
+ */
69
+ export declare function decodeImportIntentV1(encoded: string): ImportIntentV1;
70
+ export interface BuildMeosLinkOptions {
71
+ /** Override QR guard threshold (default MDP_MAX_QR_URL_LENGTH). */
72
+ maxUrlLength?: number;
73
+ }
74
+ /**
75
+ * Build full https://meos.do/databox:import:{encoded}?w={widgetId} URL.
76
+ * Degrades tier (IMG/FULL → LITE → REF) when the URL exceeds MDP_MAX_QR_URL_LENGTH.
77
+ * Widget id is query-only — never embedded in the compressed blob.
78
+ */
79
+ export declare function buildMeosLink(intent: ImportIntentV1, widgetId?: string, options?: BuildMeosLinkOptions): string;
80
+ /**
81
+ * Parse a meos.do import URL into ImportIntentV1.
82
+ * Accepts full URL or path-only `databox:import:…` segments.
83
+ * Widget attribution (?w=) is query-only and not merged into the intent.
84
+ */
85
+ export declare function decodeMeosLink(url: string): ImportIntentV1;
86
+ /** Read widget attribution from a meos.do import URL (?w= query param). */
87
+ export declare function parseWidgetAttribution(url: string): string | undefined;
88
+ //# sourceMappingURL=import-intent-v1.d.ts.map