@nowline/embed 0.5.1 → 0.7.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.
@@ -2,7 +2,24 @@ import { type ParseResult } from '@nowline/browser';
2
2
  import type { ThemeName } from '@nowline/layout';
3
3
  export interface EmbedRenderOptions {
4
4
  theme?: ThemeName;
5
- today?: Date;
5
+ /**
6
+ * "Today" override for the now-line.
7
+ *
8
+ * - `Date` — explicit UTC-midnight date; drawn as-is.
9
+ * - `string` — raw date string (YYYY-MM-DD or ISO 8601 instant with Z/offset).
10
+ * - `null` — suppress the now-line (mirrors `--now -`).
11
+ * - `undefined` — default to local today (or `timezone` if set).
12
+ *
13
+ * Previously accepted only `Date`; strings and null are new as of the
14
+ * timezone-aware now-line release.
15
+ */
16
+ today?: Date | string | null;
17
+ /**
18
+ * Timezone for the clock-based "today" default. Only consulted when
19
+ * `today` is `undefined`. Accepts `"local"` (default), `"UTC"`, ISO 8601
20
+ * offsets (`"Z"`, `"+05:30"`), or IANA names (`"America/Los_Angeles"`).
21
+ */
22
+ timezone?: string;
6
23
  locale?: string;
7
24
  width?: number;
8
25
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAcA,OAAO,EAMH,KAAK,WAAW,EACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,WAAW,kBAAkB;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IACxB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAID,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAU3E;AAED,wBAAsB,YAAY,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,MAAM,CAAC,CAwBjB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAGnB,OAAO,EAAE,MAAM,EAAE;gBADjC,OAAO,EAAE,MAAM,EACC,OAAO,EAAE,MAAM,EAAE;CAKxC;AAKD,wBAAgB,4BAA4B,IAAI,IAAI,CAGnD"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAcA,OAAO,EAMH,KAAK,WAAW,EACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,WAAW,kBAAkB;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IACxB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAID,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAU3E;AAED,wBAAsB,YAAY,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,MAAM,CAAC,CAyBjB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAGnB,OAAO,EAAE,MAAM,EAAE;gBADjC,OAAO,EAAE,MAAM,EACC,OAAO,EAAE,MAAM,EAAE;CAKxC;AAKD,wBAAgB,4BAA4B,IAAI,IAAI,CAGnD"}
package/dist/pipeline.js CHANGED
@@ -30,6 +30,7 @@ export async function renderSource(source, options = {}) {
30
30
  filePath: EMBED_SOURCE_PATH,
31
31
  theme: options.theme,
32
32
  today: options.today,
33
+ timezone: options.timezone,
33
34
  locale: options.locale,
34
35
  width: options.width,
35
36
  idPrefix: options.idPrefix,
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uEAAuE;AACvE,oCAAoC;AACpC,EAAE;AACF,iEAAiE;AACjE,0EAA0E;AAC1E,mEAAmE;AACnE,sEAAsE;AACtE,aAAa;AACb,mEAAmE;AACnE,sEAAsE;AACtE,uEAAuE;AACvE,2CAA2C;AAE3C,OAAO,EACH,8BAA8B,EAE9B,WAAW,IAAI,kBAAkB,EACjC,YAAY,IAAI,mBAAmB,GAGtC,MAAM,kBAAkB,CAAC;AAG1B,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAsB3C,IAAI,qBAAqB,GAAG,KAAK,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc;IAC5C,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE;QAC1D,QAAQ,EAAE,iBAAiB;KAC9B,CAAC,CAAC;IACH,OAAO;QACH,GAAG;QACH,MAAM,EAAE,WAAW;aACd,MAAM,CAAC,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC;aACpD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;KAC7B,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAC9B,MAAc,EACd,UAA8B,EAAE;IAEhC,MAAM,cAAc,GAAyB;QACzC,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,gBAAgB,EAAE,GAAG,EAAE;YACnB,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBACzB,qBAAqB,GAAG,IAAI,CAAC;gBAC7B,OAAO,CAAC,IAAI,CACR,qFAAqF;oBACjF,+DAA+D,CACtE,CAAC;YACN,CAAC;QACL,CAAC;KACJ,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACjE,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IAE7C,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAChG,MAAM,IAAI,gBAAgB,CAAC,oCAAoC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAGnB;IAFpB,YACI,OAAe,EACC,OAAiB;QAEjC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,YAAO,GAAP,OAAO,CAAU;QAGjC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACnC,CAAC;CACJ;AAED,uEAAuE;AACvE,wEAAwE;AACxE,iCAAiC;AACjC,MAAM,UAAU,4BAA4B;IACxC,qBAAqB,GAAG,KAAK,CAAC;IAC9B,8BAA8B,EAAE,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uEAAuE;AACvE,oCAAoC;AACpC,EAAE;AACF,iEAAiE;AACjE,0EAA0E;AAC1E,mEAAmE;AACnE,sEAAsE;AACtE,aAAa;AACb,mEAAmE;AACnE,sEAAsE;AACtE,uEAAuE;AACvE,2CAA2C;AAE3C,OAAO,EACH,8BAA8B,EAE9B,WAAW,IAAI,kBAAkB,EACjC,YAAY,IAAI,mBAAmB,GAGtC,MAAM,kBAAkB,CAAC;AAG1B,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAuC3C,IAAI,qBAAqB,GAAG,KAAK,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc;IAC5C,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE;QAC1D,QAAQ,EAAE,iBAAiB;KAC9B,CAAC,CAAC;IACH,OAAO;QACH,GAAG;QACH,MAAM,EAAE,WAAW;aACd,MAAM,CAAC,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC;aACpD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;KAC7B,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAC9B,MAAc,EACd,UAA8B,EAAE;IAEhC,MAAM,cAAc,GAAyB;QACzC,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,gBAAgB,EAAE,GAAG,EAAE;YACnB,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBACzB,qBAAqB,GAAG,IAAI,CAAC;gBAC7B,OAAO,CAAC,IAAI,CACR,qFAAqF;oBACjF,+DAA+D,CACtE,CAAC;YACN,CAAC;QACL,CAAC;KACJ,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACjE,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IAE7C,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAChG,MAAM,IAAI,gBAAgB,CAAC,oCAAoC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAGnB;IAFpB,YACI,OAAe,EACC,OAAiB;QAEjC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,YAAO,GAAP,OAAO,CAAU;QAGjC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACnC,CAAC;CACJ;AAED,uEAAuE;AACvE,wEAAwE;AACxE,iCAAiC;AACjC,MAAM,UAAU,4BAA4B;IACxC,qBAAqB,GAAG,KAAK,CAAC;IAC9B,8BAA8B,EAAE,CAAC;AACrC,CAAC"}
package/dist/share.d.ts CHANGED
@@ -1,45 +1,2 @@
1
- export declare const DEFAULT_SHARE_BASE = "https://free.nowline.io/open";
2
- /**
3
- * The `share` initialize option selects where share links point.
4
- *
5
- * - `true` — use DEFAULT_SHARE_BASE (the default).
6
- * - `string` — a base URL with optional path; built via the URL API so
7
- * `https://foo.com/open` → `https://foo.com/open#text=…`.
8
- * - `false` / `'none'` — disable the share anchor entirely.
9
- * - `{ textUrl, remoteUrl }` — escape hatch for non-hash URL shapes;
10
- * `{text}` substituted with the base64url payload, `{url}` with the
11
- * percent-encoded source URL.
12
- */
13
- export type ShareOption = boolean | 'none' | string | {
14
- textUrl: string;
15
- remoteUrl: string;
16
- };
17
- /**
18
- * Encode source text → `#text=<base64url(zlib(utf8(source)))>`.
19
- *
20
- * The return value includes the `#text=` key so callers can use it
21
- * directly as a URL fragment.
22
- *
23
- * Sync, single code path, no feature-detect.
24
- */
25
- export declare function encodeText(source: string): string;
26
- export interface BuildShareLinkOptions {
27
- /** The roadmap source text (used to build the #text= fragment). */
28
- source: string;
29
- /**
30
- * Resolved source URL for the block (per-block → global → undefined).
31
- * Only `https:` URLs are emitted as `#url=`; anything else falls
32
- * back to the inline `#text=` encoding.
33
- */
34
- sourceUrl?: string | undefined;
35
- /** The `share` option from InitializeOptions. */
36
- share: ShareOption;
37
- }
38
- /**
39
- * Build the full "Share on Nowline" URL for a rendered block.
40
- *
41
- * Returns `null` when `share` is `false` or `'none'`, signalling that
42
- * no anchor should be rendered.
43
- */
44
- export declare function buildShareLink({ source, sourceUrl, share }: BuildShareLinkOptions): string | null;
1
+ export { type BuildShareLinkOptions, buildShareLink, DEFAULT_SHARE_BASE, encodeText, type ShareOption, } from '@nowline/share-link';
45
2
  //# sourceMappingURL=share.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"share.d.ts","sourceRoot":"","sources":["../src/share.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,kBAAkB,iCAAiC,CAAC;AAEjE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAgBD,MAAM,WAAW,qBAAqB;IAClC,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,iDAAiD;IACjD,KAAK,EAAE,WAAW,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAwBjG"}
1
+ {"version":3,"file":"share.d.ts","sourceRoot":"","sources":["../src/share.ts"],"names":[],"mappings":"AAMA,OAAO,EACH,KAAK,qBAAqB,EAC1B,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,KAAK,WAAW,GACnB,MAAM,qBAAqB,CAAC"}
package/dist/share.js CHANGED
@@ -1,75 +1,7 @@
1
- // Share-link generation for the "Share on Nowline" anchor that the
2
- // auto-scan loop appends after each rendered SVG.
3
- //
4
- // Encoding grammar (normative defined in specs/embed.md):
5
- // #text=base64url(zlib(utf8(source)))
6
- // #url=<https-url>
7
- //
8
- // zlib = RFC 1950 via fflate zlibSync (byte-compatible with native
9
- // CompressionStream('deflate')); base64url strips padding and maps
10
- // +→- /→_.
11
- //
12
- // Sync, works on every browser, no feature-detect.
13
- import { zlibSync } from 'fflate';
14
- export const DEFAULT_SHARE_BASE = 'https://free.nowline.io/open';
15
- /**
16
- * Encode source text → `#text=<base64url(zlib(utf8(source)))>`.
17
- *
18
- * The return value includes the `#text=` key so callers can use it
19
- * directly as a URL fragment.
20
- *
21
- * Sync, single code path, no feature-detect.
22
- */
23
- export function encodeText(source) {
24
- return `#text=${_encodePayload(source)}`;
25
- }
26
- /** base64url(zlib(utf8(source))) without the `#text=` prefix. */
27
- function _encodePayload(source) {
28
- const bytes = new TextEncoder().encode(source);
29
- const compressed = zlibSync(bytes);
30
- // Convert Uint8Array to binary string for btoa. Chunked to avoid
31
- // call-stack limits on large payloads.
32
- const chunk = 0x8000; // 32 KB — safe below JS engine stack limits
33
- let bin = '';
34
- for (let i = 0; i < compressed.length; i += chunk) {
35
- bin += String.fromCharCode(...compressed.subarray(i, i + chunk));
36
- }
37
- return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
38
- }
39
- /**
40
- * Build the full "Share on Nowline" URL for a rendered block.
41
- *
42
- * Returns `null` when `share` is `false` or `'none'`, signalling that
43
- * no anchor should be rendered.
44
- */
45
- export function buildShareLink({ source, sourceUrl, share }) {
46
- if (share === false || share === 'none') {
47
- return null;
48
- }
49
- if (typeof share === 'object') {
50
- // Template mode: { textUrl, remoteUrl }
51
- if (sourceUrl !== undefined && _isHttps(sourceUrl)) {
52
- return share.remoteUrl.replace('{url}', encodeURIComponent(sourceUrl));
53
- }
54
- return share.textUrl.replace('{text}', _encodePayload(source));
55
- }
56
- // share === true → DEFAULT_SHARE_BASE; share is a string → custom base URL
57
- const base = share === true ? DEFAULT_SHARE_BASE : share;
58
- const url = new URL(base);
59
- if (sourceUrl !== undefined && _isHttps(sourceUrl)) {
60
- url.hash = `url=${encodeURIComponent(sourceUrl)}`;
61
- }
62
- else {
63
- url.hash = `text=${_encodePayload(source)}`;
64
- }
65
- return url.toString();
66
- }
67
- function _isHttps(url) {
68
- try {
69
- return new URL(url).protocol === 'https:';
70
- }
71
- catch {
72
- return false;
73
- }
74
- }
1
+ // Share-link generation moved to the dependency-light @nowline/share-link
2
+ // leaf so the encoder is shared verbatim with @nowline/mcp (one
3
+ // spec-normative implementation — see specs/embed.md). This file stays as a
4
+ // thin re-export so internal importers (auto-scan.ts) and the package's
5
+ // public surface are unchanged.
6
+ export { buildShareLink, DEFAULT_SHARE_BASE, encodeText, } from '@nowline/share-link';
75
7
  //# sourceMappingURL=share.js.map
package/dist/share.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"share.js","sourceRoot":"","sources":["../src/share.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,kDAAkD;AAClD,EAAE;AACF,4DAA4D;AAC5D,wCAAwC;AACxC,qBAAqB;AACrB,EAAE;AACF,mEAAmE;AACnE,mEAAmE;AACnE,WAAW;AACX,EAAE;AACF,mDAAmD;AAEnD,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,MAAM,CAAC,MAAM,kBAAkB,GAAG,8BAA8B,CAAC;AAejE;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACrC,OAAO,SAAS,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;AAC7C,CAAC;AAED,iEAAiE;AACjE,SAAS,cAAc,CAAC,MAAc;IAClC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,iEAAiE;IACjE,uCAAuC;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,4CAA4C;IAClE,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;QAChD,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC;AAeD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAyB;IAC9E,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,wCAAwC;QACxC,IAAI,SAAS,KAAK,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,2EAA2E;IAC3E,MAAM,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC;IACzD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAE1B,IAAI,SAAS,KAAK,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC,IAAI,GAAG,OAAO,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC;IACtD,CAAC;SAAM,CAAC;QACJ,GAAG,CAAC,IAAI,GAAG,QAAQ,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;IAChD,CAAC;IAED,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IACzB,IAAI,CAAC;QACD,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"share.js","sourceRoot":"","sources":["../src/share.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,gEAAgE;AAChE,4EAA4E;AAC5E,wEAAwE;AACxE,gCAAgC;AAEhC,OAAO,EAEH,cAAc,EACd,kBAAkB,EAClB,UAAU,GAEb,MAAM,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowline/embed",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "description": "Browser embed bundle for Nowline. Drop a <script> tag and ```nowline``` blocks render in place.",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -39,12 +39,12 @@
39
39
  "cdn"
40
40
  ],
41
41
  "dependencies": {
42
- "fflate": "^0.8.3",
43
42
  "langium": "~4.2.4",
44
- "@nowline/core": "0.5.1",
45
- "@nowline/layout": "0.5.1",
46
- "@nowline/browser": "0.5.1",
47
- "@nowline/renderer": "0.5.1"
43
+ "@nowline/browser": "0.7.0",
44
+ "@nowline/core": "0.7.0",
45
+ "@nowline/layout": "0.7.0",
46
+ "@nowline/renderer": "0.7.0",
47
+ "@nowline/share-link": "0.7.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^25.9.1",
package/src/auto-scan.ts CHANGED
@@ -12,7 +12,10 @@ export interface AutoScanInputs {
12
12
  theme?: ThemeName;
13
13
  locale?: string;
14
14
  width?: number;
15
- today?: Date;
15
+ /** See {@link EmbedRenderOptions.today} — accepts Date, string, null, or undefined. */
16
+ today?: Date | string | null;
17
+ /** See {@link EmbedRenderOptions.timezone}. */
18
+ timezone?: string;
16
19
  /**
17
20
  * Controls the "Share on Nowline" anchor appended after each rendered
18
21
  * SVG. Defaults to `true` when omitted (mirrors the config default).
@@ -68,6 +71,7 @@ export async function runAutoScan(inputs: AutoScanInputs): Promise<AutoScanResul
68
71
  locale: inputs.locale,
69
72
  width: inputs.width,
70
73
  today: inputs.today,
74
+ timezone: inputs.timezone,
71
75
  idPrefix,
72
76
  };
73
77
 
package/src/index.ts CHANGED
@@ -73,8 +73,18 @@ export interface InitializeOptions {
73
73
  locale?: string;
74
74
  /** Layout canvas width in pixels. Layout's default is 1280. */
75
75
  width?: number;
76
- /** Pin a `today` for deterministic snapshots; defaults to live `new Date()` per render. */
77
- today?: Date;
76
+ /**
77
+ * "Today" override for the now-line. Accepts a `Date`, a YYYY-MM-DD string,
78
+ * a full ISO 8601 instant (with Z or ±offset), or `null` to suppress the
79
+ * now-line. Defaults to the local civil date when omitted.
80
+ */
81
+ today?: Date | string | null;
82
+ /**
83
+ * Timezone for the clock-based "today" default. Only consulted when `today`
84
+ * is omitted. Accepts `"local"` (default), `"UTC"`, ISO 8601 offsets, or
85
+ * IANA timezone names (e.g. `"America/Los_Angeles"`).
86
+ */
87
+ timezone?: string;
78
88
  /**
79
89
  * Controls the "Share on Nowline" anchor appended after each rendered SVG.
80
90
  * - `true` (default) — link to the Free app open route
@@ -99,7 +109,8 @@ interface ResolvedConfig {
99
109
  selector: string;
100
110
  locale?: string;
101
111
  width?: number;
102
- today?: Date;
112
+ today?: Date | string | null;
113
+ timezone?: string;
103
114
  /** System theme captured at init; not reactive to OS theme flips mid-session. */
104
115
  systemTheme: 'light' | 'dark';
105
116
  /** Controls the "Share on Nowline" anchor. Defaults to `true`. */
@@ -126,7 +137,8 @@ export function initialize(options: InitializeOptions = {}): void {
126
137
  selector: options.selector ?? config.selector,
127
138
  locale: options.locale ?? config.locale,
128
139
  width: options.width ?? config.width,
129
- today: options.today ?? config.today,
140
+ today: options.today !== undefined ? options.today : config.today,
141
+ timezone: options.timezone ?? config.timezone,
130
142
  share: options.share ?? config.share,
131
143
  sourceUrl: options.sourceUrl ?? config.sourceUrl,
132
144
  // Re-read `prefers-color-scheme` on every initialize() so callers
@@ -144,6 +156,7 @@ function renderOptionsFromConfig(): EmbedRenderOptions {
144
156
  locale: config.locale,
145
157
  width: config.width,
146
158
  today: config.today,
159
+ timezone: config.timezone,
147
160
  };
148
161
  }
149
162
 
@@ -180,7 +193,8 @@ export async function init(overrides?: Partial<AutoScanInputs>): Promise<AutoSca
180
193
  theme: overrides?.theme ?? renderOptionsFromConfig().theme,
181
194
  locale: overrides?.locale ?? config.locale,
182
195
  width: overrides?.width ?? config.width,
183
- today: overrides?.today ?? config.today,
196
+ today: overrides?.today !== undefined ? overrides.today : config.today,
197
+ timezone: overrides?.timezone ?? config.timezone,
184
198
  share: overrides?.share ?? config.share,
185
199
  sourceUrl: overrides?.sourceUrl ?? config.sourceUrl,
186
200
  document: overrides?.document,
package/src/pipeline.ts CHANGED
@@ -26,7 +26,24 @@ const EMBED_SOURCE_PATH = '/embed.nowline';
26
26
 
27
27
  export interface EmbedRenderOptions {
28
28
  theme?: ThemeName;
29
- today?: Date;
29
+ /**
30
+ * "Today" override for the now-line.
31
+ *
32
+ * - `Date` — explicit UTC-midnight date; drawn as-is.
33
+ * - `string` — raw date string (YYYY-MM-DD or ISO 8601 instant with Z/offset).
34
+ * - `null` — suppress the now-line (mirrors `--now -`).
35
+ * - `undefined` — default to local today (or `timezone` if set).
36
+ *
37
+ * Previously accepted only `Date`; strings and null are new as of the
38
+ * timezone-aware now-line release.
39
+ */
40
+ today?: Date | string | null;
41
+ /**
42
+ * Timezone for the clock-based "today" default. Only consulted when
43
+ * `today` is `undefined`. Accepts `"local"` (default), `"UTC"`, ISO 8601
44
+ * offsets (`"Z"`, `"+05:30"`), or IANA names (`"America/Los_Angeles"`).
45
+ */
46
+ timezone?: string;
30
47
  locale?: string;
31
48
  width?: number;
32
49
  /**
@@ -66,6 +83,7 @@ export async function renderSource(
66
83
  filePath: EMBED_SOURCE_PATH,
67
84
  theme: options.theme,
68
85
  today: options.today,
86
+ timezone: options.timezone,
69
87
  locale: options.locale,
70
88
  width: options.width,
71
89
  idPrefix: options.idPrefix,
package/src/share.ts CHANGED
@@ -1,108 +1,13 @@
1
- // Share-link generation for the "Share on Nowline" anchor that the
2
- // auto-scan loop appends after each rendered SVG.
3
- //
4
- // Encoding grammar (normative defined in specs/embed.md):
5
- // #text=base64url(zlib(utf8(source)))
6
- // #url=<https-url>
7
- //
8
- // zlib = RFC 1950 via fflate zlibSync (byte-compatible with native
9
- // CompressionStream('deflate')); base64url strips padding and maps
10
- // +→- /→_.
11
- //
12
- // Sync, works on every browser, no feature-detect.
13
-
14
- import { zlibSync } from 'fflate';
15
-
16
- export const DEFAULT_SHARE_BASE = 'https://free.nowline.io/open';
17
-
18
- /**
19
- * The `share` initialize option selects where share links point.
20
- *
21
- * - `true` — use DEFAULT_SHARE_BASE (the default).
22
- * - `string` — a base URL with optional path; built via the URL API so
23
- * `https://foo.com/open` → `https://foo.com/open#text=…`.
24
- * - `false` / `'none'` — disable the share anchor entirely.
25
- * - `{ textUrl, remoteUrl }` — escape hatch for non-hash URL shapes;
26
- * `{text}` substituted with the base64url payload, `{url}` with the
27
- * percent-encoded source URL.
28
- */
29
- export type ShareOption = boolean | 'none' | string | { textUrl: string; remoteUrl: string };
30
-
31
- /**
32
- * Encode source text → `#text=<base64url(zlib(utf8(source)))>`.
33
- *
34
- * The return value includes the `#text=` key so callers can use it
35
- * directly as a URL fragment.
36
- *
37
- * Sync, single code path, no feature-detect.
38
- */
39
- export function encodeText(source: string): string {
40
- return `#text=${_encodePayload(source)}`;
41
- }
42
-
43
- /** base64url(zlib(utf8(source))) without the `#text=` prefix. */
44
- function _encodePayload(source: string): string {
45
- const bytes = new TextEncoder().encode(source);
46
- const compressed = zlibSync(bytes);
47
- // Convert Uint8Array to binary string for btoa. Chunked to avoid
48
- // call-stack limits on large payloads.
49
- const chunk = 0x8000; // 32 KB — safe below JS engine stack limits
50
- let bin = '';
51
- for (let i = 0; i < compressed.length; i += chunk) {
52
- bin += String.fromCharCode(...compressed.subarray(i, i + chunk));
53
- }
54
- return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
55
- }
56
-
57
- export interface BuildShareLinkOptions {
58
- /** The roadmap source text (used to build the #text= fragment). */
59
- source: string;
60
- /**
61
- * Resolved source URL for the block (per-block → global → undefined).
62
- * Only `https:` URLs are emitted as `#url=`; anything else falls
63
- * back to the inline `#text=` encoding.
64
- */
65
- sourceUrl?: string | undefined;
66
- /** The `share` option from InitializeOptions. */
67
- share: ShareOption;
68
- }
69
-
70
- /**
71
- * Build the full "Share on Nowline" URL for a rendered block.
72
- *
73
- * Returns `null` when `share` is `false` or `'none'`, signalling that
74
- * no anchor should be rendered.
75
- */
76
- export function buildShareLink({ source, sourceUrl, share }: BuildShareLinkOptions): string | null {
77
- if (share === false || share === 'none') {
78
- return null;
79
- }
80
-
81
- if (typeof share === 'object') {
82
- // Template mode: { textUrl, remoteUrl }
83
- if (sourceUrl !== undefined && _isHttps(sourceUrl)) {
84
- return share.remoteUrl.replace('{url}', encodeURIComponent(sourceUrl));
85
- }
86
- return share.textUrl.replace('{text}', _encodePayload(source));
87
- }
88
-
89
- // share === true → DEFAULT_SHARE_BASE; share is a string → custom base URL
90
- const base = share === true ? DEFAULT_SHARE_BASE : share;
91
- const url = new URL(base);
92
-
93
- if (sourceUrl !== undefined && _isHttps(sourceUrl)) {
94
- url.hash = `url=${encodeURIComponent(sourceUrl)}`;
95
- } else {
96
- url.hash = `text=${_encodePayload(source)}`;
97
- }
98
-
99
- return url.toString();
100
- }
101
-
102
- function _isHttps(url: string): boolean {
103
- try {
104
- return new URL(url).protocol === 'https:';
105
- } catch {
106
- return false;
107
- }
108
- }
1
+ // Share-link generation moved to the dependency-light @nowline/share-link
2
+ // leaf so the encoder is shared verbatim with @nowline/mcp (one
3
+ // spec-normative implementation — see specs/embed.md). This file stays as a
4
+ // thin re-export so internal importers (auto-scan.ts) and the package's
5
+ // public surface are unchanged.
6
+
7
+ export {
8
+ type BuildShareLinkOptions,
9
+ buildShareLink,
10
+ DEFAULT_SHARE_BASE,
11
+ encodeText,
12
+ type ShareOption,
13
+ } from '@nowline/share-link';