@momentco-ai/moment-sdk 0.2.2-dev.17 → 0.3.0-dev.18

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.
@@ -0,0 +1,166 @@
1
+ import { DEMO_FIXTURES } from './fixtures.js';
2
+
3
+ const LOG_ID = 'demo-log';
4
+ let packageMetaPromise;
5
+
6
+ function getPackageMeta() {
7
+ if (!packageMetaPromise) {
8
+ packageMetaPromise = fetch('/package-meta.json')
9
+ .then((response) => response.json())
10
+ .catch(() => ({
11
+ version: 'latest',
12
+ cdnUrl: 'https://cdn.jsdelivr.net/npm/@momentco-ai/moment-sdk',
13
+ }));
14
+ }
15
+ return packageMetaPromise;
16
+ }
17
+
18
+ function loadScript(src) {
19
+ return new Promise((resolve, reject) => {
20
+ const existing = document.querySelector(`script[data-demo-sdk-src="${src}"]`);
21
+ if (existing) {
22
+ resolve();
23
+ return;
24
+ }
25
+ const script = document.createElement('script');
26
+ script.src = src;
27
+ script.async = true;
28
+ script.dataset.demoSdkSrc = src;
29
+ script.onload = () => resolve();
30
+ script.onerror = () => reject(new Error(`Failed to load SDK: ${src}`));
31
+ document.head.appendChild(script);
32
+ });
33
+ }
34
+
35
+ export function appendLog(message, tone = 'muted') {
36
+ const panel = document.getElementById(LOG_ID);
37
+ if (!panel) return;
38
+ const line = document.createElement('p');
39
+ line.className = `log-line log-line--${tone}`;
40
+ line.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
41
+ panel.prepend(line);
42
+ }
43
+
44
+ function wireCopyButtons() {
45
+ document.querySelectorAll('[data-copy-target]').forEach((button) => {
46
+ button.addEventListener('click', async () => {
47
+ const targetId = button.getAttribute('data-copy-target');
48
+ const target = targetId ? document.getElementById(targetId) : null;
49
+ if (!target) return;
50
+ try {
51
+ await navigator.clipboard.writeText(target.textContent?.trim() ?? '');
52
+ const label = button.textContent;
53
+ button.textContent = 'Copied';
54
+ setTimeout(() => {
55
+ button.textContent = label;
56
+ }, 1500);
57
+ } catch {
58
+ appendLog('Clipboard copy failed', 'error');
59
+ }
60
+ });
61
+ });
62
+ }
63
+
64
+ export function mountNav({ title } = {}) {
65
+ const bar = document.getElementById('demo-nav');
66
+ if (!bar) return;
67
+
68
+ bar.innerHTML = `
69
+ <nav class="demo-breadcrumb" aria-label="Examples">
70
+ <a href="/">Examples</a>${title ? ` / <span>${title}</span>` : ''}
71
+ </nav>
72
+ `;
73
+ }
74
+
75
+ export async function initDemoSdk() {
76
+ appendLog('Loading SDK…');
77
+ await loadScript('/moment-sdk.js');
78
+
79
+ if (!window.MomentSdk) {
80
+ throw new Error('MomentSdk global not found after script load');
81
+ }
82
+
83
+ const sdk = new window.MomentSdk({
84
+ onSuccess: (result) => {
85
+ appendLog(`onSuccess → ${JSON.stringify(result)}`, 'success');
86
+ },
87
+ onError: (result) => {
88
+ appendLog(`onError → ${JSON.stringify(result)}`, 'error');
89
+ },
90
+ onClose: (result) => {
91
+ appendLog(`onClose → ${result.result}`, 'muted');
92
+ },
93
+ onAnalytics: (event) => {
94
+ appendLog(`analytics → ${event.event}`, 'muted');
95
+ },
96
+ });
97
+
98
+ sdk.rebind();
99
+ appendLog('MomentSdk ready — click a trigger button', 'success');
100
+ wireCopyButtons();
101
+ return sdk;
102
+ }
103
+
104
+ export function renderMomentCards(container, moments, teamSlug) {
105
+ const root = typeof container === 'string' ? document.querySelector(container) : container;
106
+ if (!root) return;
107
+
108
+ root.innerHTML = moments
109
+ .map(
110
+ (moment) => `
111
+ <article class="card">
112
+ <div class="card-info">
113
+ <div class="card-title">${moment.title}</div>
114
+ <div class="card-meta">${moment.date} · <code>${moment.slug}</code></div>
115
+ </div>
116
+ <button
117
+ type="button"
118
+ class="moment-sync-trigger"
119
+ data-moment-team-slug="${teamSlug}"
120
+ data-moment-slug="${moment.slug}"
121
+ data-moment-trigger-type="moment"
122
+ >
123
+ Add to Calendar
124
+ </button>
125
+ </article>
126
+ `,
127
+ )
128
+ .join('');
129
+ }
130
+
131
+ export function mountLogPanel(label = 'SDK callbacks') {
132
+ const host = document.getElementById('demo-log-host');
133
+ if (!host) return;
134
+ host.innerHTML = `
135
+ <section class="section">
136
+ <h2>${label}</h2>
137
+ <div id="${LOG_ID}" class="log-panel" aria-live="polite">
138
+ <p class="log-line log-line--muted">Waiting for SDK events…</p>
139
+ </div>
140
+ </section>
141
+ `;
142
+ }
143
+
144
+ export async function embedSnippetHtml({ triggerType, momentSlug, listSlug, label }) {
145
+ const meta = await getPackageMeta();
146
+ const attrs = [
147
+ `class="moment-sync-trigger"`,
148
+ `data-moment-team-slug="${DEMO_FIXTURES.teamSlug}"`,
149
+ `data-moment-trigger-type="${triggerType}"`,
150
+ ];
151
+ if (momentSlug) attrs.push(`data-moment-slug="${momentSlug}"`);
152
+ if (listSlug) attrs.push(`data-moment-list-slug="${listSlug}"`);
153
+
154
+ return `<!-- Moment SDK trigger -->
155
+ <button type="button"
156
+ ${attrs.join('\n ')}
157
+ >${label ?? 'Add to Calendar'}</button>
158
+
159
+ <script src="${meta.cdnUrl}"></script>
160
+ <script>
161
+ new MomentSdk({
162
+ onSuccess: (r) => console.log('Synced', r),
163
+ onError: (r) => console.error('Failed', r),
164
+ });
165
+ </script>`;
166
+ }
@@ -0,0 +1,270 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --bg: #fafaf9;
4
+ --surface: #ffffff;
5
+ --border: #e7e5e4;
6
+ --text: #1c1917;
7
+ --muted: #78716c;
8
+ --accent: #171717;
9
+ --accent-soft: #f5f5f4;
10
+ --code: #44403c;
11
+ --success: #15803d;
12
+ --error: #b91c1c;
13
+ --radius: 12px;
14
+ --font: system-ui, -apple-system, 'Segoe UI', sans-serif;
15
+ --mono: ui-monospace, 'SF Mono', Menlo, monospace;
16
+ }
17
+
18
+ * {
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ body {
23
+ margin: 0;
24
+ font-family: var(--font);
25
+ background: var(--bg);
26
+ color: var(--text);
27
+ line-height: 1.5;
28
+ }
29
+
30
+ a {
31
+ color: inherit;
32
+ }
33
+
34
+ .demo-shell {
35
+ max-width: 720px;
36
+ margin: 0 auto;
37
+ padding: 32px 20px 48px;
38
+ }
39
+
40
+ .demo-top {
41
+ display: flex;
42
+ flex-wrap: wrap;
43
+ align-items: center;
44
+ justify-content: space-between;
45
+ gap: 12px;
46
+ margin-bottom: 24px;
47
+ }
48
+
49
+ .demo-breadcrumb {
50
+ font-size: 13px;
51
+ color: var(--muted);
52
+ }
53
+
54
+ .demo-breadcrumb a {
55
+ text-decoration: none;
56
+ }
57
+
58
+ .demo-breadcrumb a:hover {
59
+ text-decoration: underline;
60
+ }
61
+
62
+ .demo-pills {
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ gap: 8px;
66
+ }
67
+
68
+ .pill {
69
+ display: inline-flex;
70
+ align-items: center;
71
+ gap: 6px;
72
+ padding: 4px 10px;
73
+ border-radius: 999px;
74
+ border: 1px solid var(--border);
75
+ background: var(--surface);
76
+ font-size: 12px;
77
+ color: var(--muted);
78
+ }
79
+
80
+ .pill strong {
81
+ color: var(--text);
82
+ font-weight: 600;
83
+ }
84
+
85
+ .pill a {
86
+ color: var(--text);
87
+ text-decoration: underline;
88
+ text-underline-offset: 2px;
89
+ }
90
+
91
+ h1 {
92
+ margin: 0 0 8px;
93
+ font-size: 28px;
94
+ letter-spacing: -0.02em;
95
+ }
96
+
97
+ .lead {
98
+ margin: 0 0 28px;
99
+ color: var(--muted);
100
+ font-size: 15px;
101
+ }
102
+
103
+ .card-grid {
104
+ display: grid;
105
+ gap: 12px;
106
+ }
107
+
108
+ .card {
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: space-between;
112
+ gap: 16px;
113
+ padding: 16px;
114
+ border: 1px solid var(--border);
115
+ border-radius: var(--radius);
116
+ background: var(--surface);
117
+ }
118
+
119
+ .card-info {
120
+ min-width: 0;
121
+ }
122
+
123
+ .card-title {
124
+ font-size: 15px;
125
+ font-weight: 600;
126
+ }
127
+
128
+ .card-meta {
129
+ margin-top: 4px;
130
+ font-size: 13px;
131
+ color: var(--muted);
132
+ }
133
+
134
+ .hero-card {
135
+ display: flex;
136
+ flex-direction: column;
137
+ align-items: flex-start;
138
+ gap: 16px;
139
+ padding: 20px;
140
+ border: 1px solid var(--border);
141
+ border-radius: var(--radius);
142
+ background: var(--surface);
143
+ }
144
+
145
+ .moment-sync-trigger,
146
+ .demo-btn {
147
+ flex-shrink: 0;
148
+ border: 1px solid var(--accent);
149
+ border-radius: 10px;
150
+ background: var(--accent);
151
+ color: #fff;
152
+ padding: 10px 16px;
153
+ font: inherit;
154
+ font-size: 14px;
155
+ font-weight: 600;
156
+ cursor: pointer;
157
+ white-space: nowrap;
158
+ }
159
+
160
+ .moment-sync-trigger:hover,
161
+ .demo-btn:hover {
162
+ opacity: 0.92;
163
+ }
164
+
165
+ .hub-grid {
166
+ display: grid;
167
+ gap: 16px;
168
+ }
169
+
170
+ .hub-card {
171
+ display: block;
172
+ padding: 20px;
173
+ border: 1px solid var(--border);
174
+ border-radius: var(--radius);
175
+ background: var(--surface);
176
+ text-decoration: none;
177
+ transition:
178
+ border-color 0.15s ease,
179
+ box-shadow 0.15s ease;
180
+ }
181
+
182
+ .hub-card:hover {
183
+ border-color: #d6d3d1;
184
+ box-shadow: 0 8px 24px rgba(28, 25, 23, 0.06);
185
+ }
186
+
187
+ .hub-card h2 {
188
+ margin: 0 0 6px;
189
+ font-size: 18px;
190
+ }
191
+
192
+ .hub-card p {
193
+ margin: 0;
194
+ color: var(--muted);
195
+ font-size: 14px;
196
+ }
197
+
198
+ .section {
199
+ margin-top: 32px;
200
+ }
201
+
202
+ .section h2 {
203
+ margin: 0 0 12px;
204
+ font-size: 16px;
205
+ }
206
+
207
+ .log-panel {
208
+ margin-top: 12px;
209
+ border: 1px solid var(--border);
210
+ border-radius: var(--radius);
211
+ background: #0c0a09;
212
+ color: #e7e5e4;
213
+ font-family: var(--mono);
214
+ font-size: 12px;
215
+ line-height: 1.6;
216
+ max-height: 220px;
217
+ overflow: auto;
218
+ padding: 12px;
219
+ }
220
+
221
+ .log-line {
222
+ margin: 0 0 6px;
223
+ word-break: break-word;
224
+ }
225
+
226
+ .log-line--success {
227
+ color: #86efac;
228
+ }
229
+
230
+ .log-line--error {
231
+ color: #fca5a5;
232
+ }
233
+
234
+ .log-line--muted {
235
+ color: #a8a29e;
236
+ }
237
+
238
+ .snippet {
239
+ position: relative;
240
+ margin-top: 8px;
241
+ }
242
+
243
+ .snippet pre {
244
+ margin: 0;
245
+ padding: 14px 16px;
246
+ border: 1px solid var(--border);
247
+ border-radius: var(--radius);
248
+ background: var(--accent-soft);
249
+ overflow-x: auto;
250
+ font-family: var(--mono);
251
+ font-size: 12px;
252
+ line-height: 1.55;
253
+ color: var(--code);
254
+ }
255
+
256
+ .copy-btn {
257
+ position: absolute;
258
+ top: 8px;
259
+ right: 8px;
260
+ border: 1px solid var(--border);
261
+ border-radius: 8px;
262
+ background: var(--surface);
263
+ padding: 4px 10px;
264
+ font-size: 12px;
265
+ cursor: pointer;
266
+ }
267
+
268
+ .copy-btn:hover {
269
+ background: var(--bg);
270
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Demo slugs for the sdk-demo fixture brand.
3
+ * Update here when your Moment account uses different slugs.
4
+ */
5
+ export const DEMO_FIXTURES = {
6
+ teamSlug: 'sdk-demo',
7
+ moments: [
8
+ {
9
+ slug: 'sdk-demo-moment-1',
10
+ title: 'SDK Demo - Moment 1',
11
+ date: 'Jun 1, 2027 · 10:00 AM ET',
12
+ },
13
+ {
14
+ slug: 'copy-sdk-demo-moment-1',
15
+ title: 'SDK Demo - Moment 2',
16
+ date: 'Jun 1, 2028 · 10:00 AM ET',
17
+ },
18
+ ],
19
+ collection: {
20
+ slug: 'sdk-demo-collection',
21
+ title: 'SDK Demo Collection',
22
+ description: 'Curated set of demo events for list-level sync.',
23
+ },
24
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momentco-ai/moment-sdk",
3
- "version": "0.2.2-dev.17",
3
+ "version": "0.3.0-dev.18",
4
4
  "type": "module",
5
5
  "description": "Embeddable calendar sync widget for external team websites",
6
6
  "author": "Moment Co. <support@momentco.ai> (https://momentco.ai)",
@@ -38,8 +38,14 @@
38
38
  "default": "./dist/moment-sdk.js"
39
39
  }
40
40
  },
41
+ "bin": {
42
+ "moment-sdk-examples": "./examples/serve.mjs"
43
+ },
41
44
  "files": [
42
- "dist"
45
+ "dist",
46
+ "examples",
47
+ "CHANGELOG.md",
48
+ "README.md"
43
49
  ],
44
50
  "publishConfig": {
45
51
  "access": "public",
@@ -56,6 +62,7 @@
56
62
  "format": "prettier --write .",
57
63
  "format:check": "prettier --check .",
58
64
  "quality": "pnpm run type-check && pnpm run lint && pnpm run format",
65
+ "examples": "pnpm build && node examples/serve.mjs",
59
66
  "prepare": "husky"
60
67
  },
61
68
  "devDependencies": {