@ai-react-markdown/core 1.2.8 → 1.3.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/dist/index.d.cts CHANGED
@@ -1,6 +1,22 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ComponentType, PropsWithChildren, CSSProperties } from 'react';
3
- import { Components } from 'react-markdown';
2
+ import { JSX, ComponentType, PropsWithChildren, CSSProperties } from 'react';
3
+ import { Element } from 'hast';
4
+
5
+ /**
6
+ * Public types for the local Markdown wrapper. Ported 1:1 from react-markdown
7
+ * v10's lib/index.js JSDoc, restructured as TypeScript declarations.
8
+ *
9
+ * @module components/markdown/types
10
+ */
11
+
12
+ /** Extra fields the wrapper passes to user-supplied tag components. */
13
+ interface ExtraProps {
14
+ node?: Element | undefined;
15
+ }
16
+ /** Map tag names to user components or other tag names. */
17
+ type Components = {
18
+ [Key in keyof JSX.IntrinsicElements]?: ComponentType<JSX.IntrinsicElements[Key] & ExtraProps> | keyof JSX.IntrinsicElements;
19
+ };
4
20
 
5
21
  /**
6
22
  * Core type definitions, enums, and default configuration for ai-react-markdown.
@@ -14,9 +30,10 @@ import { Components } from 'react-markdown';
14
30
 
15
31
  /**
16
32
  * Custom component overrides for the markdown renderer.
17
- * Alias for `react-markdown`'s `Components` type, re-exported under the
18
- * library's `AIMarkdown` naming convention so consumers don't need a
19
- * direct `react-markdown` dependency for type imports.
33
+ * Alias for the local Markdown wrapper's `Components` type (a vendored fork of
34
+ * react-markdown's), re-exported under the library's `AIMarkdown` naming
35
+ * convention so consumers don't need a direct `react-markdown` dependency
36
+ * for type imports.
20
37
  */
21
38
  type AIMarkdownCustomComponents = Components;
22
39
  /**
@@ -27,9 +44,7 @@ declare enum AIMarkdownRenderExtraSyntax {
27
44
  /** `==Highlight==` syntax support. */
28
45
  HIGHLIGHT = "HIGHLIGHT",
29
46
  /** Definition list syntax. @see https://michelf.ca/projects/php-markdown/extra/#def-list */
30
- DEFINITION_LIST = "DEFINITION_LIST",
31
- /** Superscript (`^text^`) and subscript (`~text~`) syntax. */
32
- SUBSCRIPT = "SUBSCRIPT"
47
+ DEFINITION_LIST = "DEFINITION_LIST"
33
48
  }
34
49
  /**
35
50
  * Display optimization abilities applied during markdown processing.
@@ -46,18 +61,49 @@ declare enum AIMarkdownRenderDisplayOptimizeAbility {
46
61
  /**
47
62
  * Configuration object controlling which markdown extensions and
48
63
  * display optimizations are active during rendering.
64
+ *
65
+ * Arrays are typed `readonly` so the interface is assignable from the frozen
66
+ * {@link defaultAIMarkdownRenderConfig}. Consumers can still pass mutable
67
+ * arrays since `readonly T[]` is assignable from `T[]`. Note: this is a
68
+ * compile-time hint only — user-supplied configs are not deep-frozen at
69
+ * runtime, so the library does not guarantee the object remains unchanged
70
+ * after it is passed in.
49
71
  */
50
72
  interface AIMarkdownRenderConfig {
51
73
  /** Extra syntax extensions to enable. */
52
- extraSyntaxSupported: AIMarkdownRenderExtraSyntax[];
74
+ readonly extraSyntaxSupported: readonly AIMarkdownRenderExtraSyntax[];
53
75
  /** Display optimization abilities to enable. */
54
- displayOptimizeAbilities: AIMarkdownRenderDisplayOptimizeAbility[];
76
+ readonly displayOptimizeAbilities: readonly AIMarkdownRenderDisplayOptimizeAbility[];
77
+ /**
78
+ * Whether to enable block-level memoization across renders.
79
+ *
80
+ * When `true` (default), the renderer splits each rendered document into
81
+ * per-block units and memoizes the React subtree of each block by its
82
+ * source identity (`raw + occurrence + ctx + position`). Unchanged blocks
83
+ * during streaming skip `toJsxRuntime` and React reconcile work, reducing
84
+ * per-frame cost roughly proportional to the unchanged fraction of the
85
+ * document. Output is byte-identical to the disabled path.
86
+ *
87
+ * When `false`, the renderer falls back to the legacy bare `<Markdown>`
88
+ * flow — every render runs the full pipeline end-to-end with no
89
+ * cross-frame reuse. Useful for debugging, for environments where the
90
+ * extra `useRef`-backed cache is undesirable, or as an escape hatch if a
91
+ * future custom rehype plugin interacts badly with the plan abstraction.
92
+ *
93
+ * @default true
94
+ */
95
+ readonly blockMemoEnabled: boolean;
55
96
  }
56
97
  /**
57
98
  * Sensible default configuration with all extensions and optimizations enabled.
58
- * Frozen to prevent accidental mutation.
99
+ * Frozen at both the top level and the inner arrays so this shared singleton
100
+ * cannot be mutated by any consumer.
59
101
  */
60
- declare const defaultAIMarkdownRenderConfig: AIMarkdownRenderConfig;
102
+ declare const defaultAIMarkdownRenderConfig: Readonly<{
103
+ extraSyntaxSupported: readonly AIMarkdownRenderExtraSyntax[];
104
+ displayOptimizeAbilities: readonly AIMarkdownRenderDisplayOptimizeAbility[];
105
+ blockMemoEnabled: true;
106
+ }>;
61
107
  /**
62
108
  * Arbitrary metadata that consumers can pass through a dedicated React context.
63
109
  * Custom renderers can access this via the {@link useAIMarkdownMetadata} hook.
@@ -140,6 +186,22 @@ interface AIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkd
140
186
  variant: AIMarkdownVariant;
141
187
  /** Active color scheme. */
142
188
  colorScheme: AIMarkdownColorScheme;
189
+ /**
190
+ * Stable identifier unique to this *logical markdown document*. Used as
191
+ * the id namespace for clobberable attributes (`id`, hash hrefs) so two
192
+ * documents on the same page don't cross-link — e.g. clicking a footnote
193
+ * `[^1]` in message A won't scroll to the `[^1]` definition in message B.
194
+ *
195
+ * Named `documentId` (not `instanceId`) intentionally: when one logical
196
+ * markdown document is split into multiple `<AIMarkdown>` instances
197
+ * (chunked / streamed rendering), every chunk SHOULD share the same value
198
+ * so id-prefixes align across the chunks.
199
+ *
200
+ * Auto-generated via React's `useId()` (SSR-safe, stable across
201
+ * re-renders) when not provided by the consumer; consumer-supplied values
202
+ * always win.
203
+ */
204
+ documentId: string;
143
205
  /** Active render configuration. */
144
206
  config: TConfig;
145
207
  }
@@ -494,16 +556,59 @@ type PartialObjectDeep<ObjectType extends object, Options extends Required<Parti
494
556
  * Must be called inside a component rendered as a descendant of `<AIMarkdown>`.
495
557
  * Throws if called outside the provider boundary.
496
558
  *
497
- * @typeParam TConfig - Expected configuration shape (defaults to {@link AIMarkdownRenderConfig}).
498
- * @returns The current render state (does not include metadata — use {@link useAIMarkdownMetadata} for that).
559
+ * ### `TConfig` is a caller-asserted type, not a derived one
499
560
  *
500
- * @example
561
+ * The generic parameter is **an assertion the caller makes about the provider
562
+ * above it** — TypeScript cannot verify that the actual `<AIMarkdown>` in the
563
+ * tree was configured with a matching `defaultConfig: TConfig`. If you pass a
564
+ * wider `TConfig` than what the provider actually carries, field access at
565
+ * compile time will look fine but resolve to `undefined` at runtime.
566
+ *
567
+ * The intended pattern is that extension packages (e.g. `@ai-react-markdown/mantine`)
568
+ * ship their own narrow wrapper hook alongside a matching `defaultConfig`, so the
569
+ * assertion is made *once* next to the provider configuration and consumers of the
570
+ * wrapper never touch the raw generic.
571
+ *
572
+ * @typeParam TConfig - Caller-asserted configuration shape (defaults to
573
+ * {@link AIMarkdownRenderConfig}). Must be aligned with the provider's
574
+ * `defaultConfig` — the library does not check this at runtime.
575
+ * @returns The current render state (does not include metadata — use
576
+ * {@link useAIMarkdownMetadata} for that).
577
+ * @throws If called outside an `<AIMarkdown>` provider tree.
578
+ *
579
+ * @example Base usage — no generic, always safe:
501
580
  * ```tsx
502
581
  * function CustomCodeBlock({ children }: PropsWithChildren) {
503
582
  * const { streaming, config } = useAIMarkdownRenderState();
504
- * // ...
583
+ * // config: AIMarkdownRenderConfig — guaranteed shape
584
+ * }
585
+ * ```
586
+ *
587
+ * @example Wrapper-hook pattern — the intended way to use an extended TConfig:
588
+ * ```tsx
589
+ * // In your extension package (pin the assertion in one place):
590
+ * interface ExtendedConfig extends AIMarkdownRenderConfig {
591
+ * themeMode: 'light' | 'dark' | 'auto';
505
592
  * }
593
+ * export const extendedDefaultConfig: ExtendedConfig = {
594
+ * ...defaultAIMarkdownRenderConfig,
595
+ * themeMode: 'auto',
596
+ * };
597
+ * export const useExtendedRenderState = () =>
598
+ * useAIMarkdownRenderState<ExtendedConfig>();
599
+ *
600
+ * // Provider is always configured with the matching defaultConfig:
601
+ * <AIMarkdown defaultConfig={extendedDefaultConfig} ...>{children}</AIMarkdown>
602
+ *
603
+ * // Consumers use the narrow wrapper — no raw generic anywhere:
604
+ * const { config } = useExtendedRenderState();
605
+ * config.themeMode; // correctly typed and present at runtime
506
606
  * ```
607
+ *
608
+ * @see `@ai-react-markdown/mantine` — real-world reference. Its
609
+ * `MantineAIMarkdownRenderConfig`, `defaultMantineAIMarkdownRenderConfig`,
610
+ * `<MantineAIMarkdown>` (which passes `defaultConfig` by default), and
611
+ * `useMantineAIMarkdownRenderState` implement this exact pattern.
507
612
  */
508
613
  declare function useAIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>(): AIMarkdownRenderState<TConfig>;
509
614
  /**
@@ -513,8 +618,24 @@ declare function useAIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig
513
618
  * do not cause re-renders in components that only consume render state
514
619
  * (e.g. {@link MarkdownContent}).
515
620
  *
516
- * @typeParam TMetadata - Expected metadata shape (defaults to {@link AIMarkdownMetadata}).
621
+ * ### `TMetadata` is a caller-asserted type
622
+ *
623
+ * Same contract as {@link useAIMarkdownRenderState} — the generic is an
624
+ * assertion about the `metadata` prop passed to the provider above, not a
625
+ * value TypeScript can derive. Unlike render-state config, metadata has no
626
+ * runtime fallback: if the provider received no `metadata`, the hook returns
627
+ * `undefined` regardless of the asserted type. Prefer wrapping this hook in
628
+ * a project-local hook that pins `TMetadata` next to the call site that
629
+ * actually provides the metadata.
630
+ *
631
+ * @typeParam TMetadata - Caller-asserted metadata shape (defaults to
632
+ * {@link AIMarkdownMetadata}). Caller is responsible for ensuring the
633
+ * provider's `metadata` prop matches this shape.
517
634
  * @returns The current metadata, or `undefined` if none was provided.
635
+ *
636
+ * @see `@ai-react-markdown/mantine` — `useMantineAIMarkdownMetadata` applies
637
+ * the wrapper pattern to this hook, pinning `MantineAIMarkdownMetadata` in
638
+ * a single location.
518
639
  */
519
640
  declare function useAIMarkdownMetadata<TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata>(): TMetadata | undefined;
520
641
  /** Props for {@link AIMarkdownRenderStateProvider}. */
@@ -523,6 +644,19 @@ interface AIMarkdownRenderStateProviderProps<TConfig extends AIMarkdownRenderCon
523
644
  fontSize: string;
524
645
  variant: AIMarkdownVariant;
525
646
  colorScheme: AIMarkdownColorScheme;
647
+ /**
648
+ * Logical-document identifier used as the id namespace for clobberable
649
+ * attributes (id / hash hrefs). Optional — when omitted, the provider
650
+ * auto-generates one via {@link useId} so the provider stays drop-in
651
+ * usable for direct consumers (e.g. extension packages that don't go
652
+ * through `<AIMarkdown>`).
653
+ *
654
+ * Pass the SAME value to multiple providers / `<AIMarkdown>` instances
655
+ * when they render chunks of the same logical document — their id
656
+ * prefixes will align so cross-chunk anchors and (once the parser sees
657
+ * the full doc) footnote navigation work.
658
+ */
659
+ documentId?: string;
526
660
  /**
527
661
  * Base default config to merge against. When omitted, falls back to
528
662
  * {@link defaultAIMarkdownRenderConfig}. Sub-packages (e.g. mantine) can
@@ -572,6 +706,14 @@ type AIMDContentPreprocessor = (content: string) => string;
572
706
  * returned, preventing unnecessary re-renders in downstream `useMemo` / `useEffect`
573
707
  * consumers that depend on reference equality.
574
708
  *
709
+ * The ref is updated in a layout effect (not during render) so that the cached
710
+ * reference only advances on COMMITTED renders. In concurrent mode a render
711
+ * may be discarded (e.g. by Suspense); writing to the ref during render would
712
+ * let values from discarded renders pollute the cache and leak into subsequent
713
+ * committed renders. Layout effects run synchronously right after commit, which
714
+ * closes the window where a same-tick re-render could otherwise observe a stale
715
+ * `ref.current` and hand back an outdated reference.
716
+ *
575
717
  * @typeParam T - The value type.
576
718
  * @param value - The potentially new value to stabilize.
577
719
  * @returns The previous reference when deep-equal, otherwise the new value.
@@ -632,12 +774,34 @@ interface AIMarkdownProps<TConfig extends AIMarkdownRenderConfig = AIMarkdownRen
632
774
  variant?: AIMarkdownVariant;
633
775
  /** Color scheme name. Defaults to `'light'`. */
634
776
  colorScheme?: AIMarkdownColorScheme;
777
+ /**
778
+ * Stable identifier for the *logical markdown document* this `<AIMarkdown>`
779
+ * is rendering. Used as the id namespace for all clobberable attributes
780
+ * (`id`, hash hrefs) so two documents on the same page do not cross-link —
781
+ * e.g. clicking a footnote `[^1]` in message A will not scroll to the
782
+ * `[^1]` definition in message B.
783
+ *
784
+ * Why `documentId` and not `instanceId`: when one logical document is
785
+ * split across multiple `<AIMarkdown>` instances (chunked / streamed
786
+ * rendering), every chunk should share the SAME `documentId` so their
787
+ * id-prefixes line up. The id is per-document, not per-React-instance.
788
+ *
789
+ * When omitted, an id is auto-generated via React's `useId()` (SSR-safe
790
+ * and stable across re-renders). Pass an explicit value when you need
791
+ * deterministic ids (snapshot tests, cross-component deep links) or when
792
+ * multiple instances render the same logical document.
793
+ *
794
+ * Consumer-supplied values pass through `encodeURIComponent` at the prefix
795
+ * construction site, so any string is safe — including ids with reserved
796
+ * characters like `:`, `/`, or spaces.
797
+ */
798
+ documentId?: string;
635
799
  }
636
800
  /**
637
801
  * Root component that preprocesses markdown content and renders it through
638
802
  * a configurable remark/rehype pipeline wrapped in typography and style layers.
639
803
  */
640
- declare const AIMarkdownComponent: <TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig, TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata>({ streaming, content, fontSize, contentPreprocessors, customComponents, defaultConfig, config, metadata, Typography, ExtraStyles, variant, colorScheme, }: AIMarkdownProps<TConfig, TRenderData>) => react_jsx_runtime.JSX.Element;
804
+ declare const AIMarkdownComponent: <TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig, TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata>({ streaming, content, fontSize, contentPreprocessors, customComponents, defaultConfig, config, metadata, Typography, ExtraStyles, variant, colorScheme, documentId, }: AIMarkdownProps<TConfig, TRenderData>) => react_jsx_runtime.JSX.Element;
641
805
  declare const _default: typeof AIMarkdownComponent;
642
806
 
643
807
  export { type AIMDContentPreprocessor, type AIMarkdownColorScheme, type AIMarkdownCustomComponents, type AIMarkdownExtraStylesComponent, type AIMarkdownExtraStylesProps, type AIMarkdownMetadata, type AIMarkdownProps, type AIMarkdownRenderConfig, AIMarkdownRenderDisplayOptimizeAbility, AIMarkdownRenderExtraSyntax, type AIMarkdownRenderState, type AIMarkdownTypographyComponent, type AIMarkdownTypographyProps, type AIMarkdownVariant, type PartialDeep, _default as default, defaultAIMarkdownRenderConfig, useAIMarkdownMetadata, useAIMarkdownRenderState, useStableValue };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,22 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ComponentType, PropsWithChildren, CSSProperties } from 'react';
3
- import { Components } from 'react-markdown';
2
+ import { JSX, ComponentType, PropsWithChildren, CSSProperties } from 'react';
3
+ import { Element } from 'hast';
4
+
5
+ /**
6
+ * Public types for the local Markdown wrapper. Ported 1:1 from react-markdown
7
+ * v10's lib/index.js JSDoc, restructured as TypeScript declarations.
8
+ *
9
+ * @module components/markdown/types
10
+ */
11
+
12
+ /** Extra fields the wrapper passes to user-supplied tag components. */
13
+ interface ExtraProps {
14
+ node?: Element | undefined;
15
+ }
16
+ /** Map tag names to user components or other tag names. */
17
+ type Components = {
18
+ [Key in keyof JSX.IntrinsicElements]?: ComponentType<JSX.IntrinsicElements[Key] & ExtraProps> | keyof JSX.IntrinsicElements;
19
+ };
4
20
 
5
21
  /**
6
22
  * Core type definitions, enums, and default configuration for ai-react-markdown.
@@ -14,9 +30,10 @@ import { Components } from 'react-markdown';
14
30
 
15
31
  /**
16
32
  * Custom component overrides for the markdown renderer.
17
- * Alias for `react-markdown`'s `Components` type, re-exported under the
18
- * library's `AIMarkdown` naming convention so consumers don't need a
19
- * direct `react-markdown` dependency for type imports.
33
+ * Alias for the local Markdown wrapper's `Components` type (a vendored fork of
34
+ * react-markdown's), re-exported under the library's `AIMarkdown` naming
35
+ * convention so consumers don't need a direct `react-markdown` dependency
36
+ * for type imports.
20
37
  */
21
38
  type AIMarkdownCustomComponents = Components;
22
39
  /**
@@ -27,9 +44,7 @@ declare enum AIMarkdownRenderExtraSyntax {
27
44
  /** `==Highlight==` syntax support. */
28
45
  HIGHLIGHT = "HIGHLIGHT",
29
46
  /** Definition list syntax. @see https://michelf.ca/projects/php-markdown/extra/#def-list */
30
- DEFINITION_LIST = "DEFINITION_LIST",
31
- /** Superscript (`^text^`) and subscript (`~text~`) syntax. */
32
- SUBSCRIPT = "SUBSCRIPT"
47
+ DEFINITION_LIST = "DEFINITION_LIST"
33
48
  }
34
49
  /**
35
50
  * Display optimization abilities applied during markdown processing.
@@ -46,18 +61,49 @@ declare enum AIMarkdownRenderDisplayOptimizeAbility {
46
61
  /**
47
62
  * Configuration object controlling which markdown extensions and
48
63
  * display optimizations are active during rendering.
64
+ *
65
+ * Arrays are typed `readonly` so the interface is assignable from the frozen
66
+ * {@link defaultAIMarkdownRenderConfig}. Consumers can still pass mutable
67
+ * arrays since `readonly T[]` is assignable from `T[]`. Note: this is a
68
+ * compile-time hint only — user-supplied configs are not deep-frozen at
69
+ * runtime, so the library does not guarantee the object remains unchanged
70
+ * after it is passed in.
49
71
  */
50
72
  interface AIMarkdownRenderConfig {
51
73
  /** Extra syntax extensions to enable. */
52
- extraSyntaxSupported: AIMarkdownRenderExtraSyntax[];
74
+ readonly extraSyntaxSupported: readonly AIMarkdownRenderExtraSyntax[];
53
75
  /** Display optimization abilities to enable. */
54
- displayOptimizeAbilities: AIMarkdownRenderDisplayOptimizeAbility[];
76
+ readonly displayOptimizeAbilities: readonly AIMarkdownRenderDisplayOptimizeAbility[];
77
+ /**
78
+ * Whether to enable block-level memoization across renders.
79
+ *
80
+ * When `true` (default), the renderer splits each rendered document into
81
+ * per-block units and memoizes the React subtree of each block by its
82
+ * source identity (`raw + occurrence + ctx + position`). Unchanged blocks
83
+ * during streaming skip `toJsxRuntime` and React reconcile work, reducing
84
+ * per-frame cost roughly proportional to the unchanged fraction of the
85
+ * document. Output is byte-identical to the disabled path.
86
+ *
87
+ * When `false`, the renderer falls back to the legacy bare `<Markdown>`
88
+ * flow — every render runs the full pipeline end-to-end with no
89
+ * cross-frame reuse. Useful for debugging, for environments where the
90
+ * extra `useRef`-backed cache is undesirable, or as an escape hatch if a
91
+ * future custom rehype plugin interacts badly with the plan abstraction.
92
+ *
93
+ * @default true
94
+ */
95
+ readonly blockMemoEnabled: boolean;
55
96
  }
56
97
  /**
57
98
  * Sensible default configuration with all extensions and optimizations enabled.
58
- * Frozen to prevent accidental mutation.
99
+ * Frozen at both the top level and the inner arrays so this shared singleton
100
+ * cannot be mutated by any consumer.
59
101
  */
60
- declare const defaultAIMarkdownRenderConfig: AIMarkdownRenderConfig;
102
+ declare const defaultAIMarkdownRenderConfig: Readonly<{
103
+ extraSyntaxSupported: readonly AIMarkdownRenderExtraSyntax[];
104
+ displayOptimizeAbilities: readonly AIMarkdownRenderDisplayOptimizeAbility[];
105
+ blockMemoEnabled: true;
106
+ }>;
61
107
  /**
62
108
  * Arbitrary metadata that consumers can pass through a dedicated React context.
63
109
  * Custom renderers can access this via the {@link useAIMarkdownMetadata} hook.
@@ -140,6 +186,22 @@ interface AIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkd
140
186
  variant: AIMarkdownVariant;
141
187
  /** Active color scheme. */
142
188
  colorScheme: AIMarkdownColorScheme;
189
+ /**
190
+ * Stable identifier unique to this *logical markdown document*. Used as
191
+ * the id namespace for clobberable attributes (`id`, hash hrefs) so two
192
+ * documents on the same page don't cross-link — e.g. clicking a footnote
193
+ * `[^1]` in message A won't scroll to the `[^1]` definition in message B.
194
+ *
195
+ * Named `documentId` (not `instanceId`) intentionally: when one logical
196
+ * markdown document is split into multiple `<AIMarkdown>` instances
197
+ * (chunked / streamed rendering), every chunk SHOULD share the same value
198
+ * so id-prefixes align across the chunks.
199
+ *
200
+ * Auto-generated via React's `useId()` (SSR-safe, stable across
201
+ * re-renders) when not provided by the consumer; consumer-supplied values
202
+ * always win.
203
+ */
204
+ documentId: string;
143
205
  /** Active render configuration. */
144
206
  config: TConfig;
145
207
  }
@@ -494,16 +556,59 @@ type PartialObjectDeep<ObjectType extends object, Options extends Required<Parti
494
556
  * Must be called inside a component rendered as a descendant of `<AIMarkdown>`.
495
557
  * Throws if called outside the provider boundary.
496
558
  *
497
- * @typeParam TConfig - Expected configuration shape (defaults to {@link AIMarkdownRenderConfig}).
498
- * @returns The current render state (does not include metadata — use {@link useAIMarkdownMetadata} for that).
559
+ * ### `TConfig` is a caller-asserted type, not a derived one
499
560
  *
500
- * @example
561
+ * The generic parameter is **an assertion the caller makes about the provider
562
+ * above it** — TypeScript cannot verify that the actual `<AIMarkdown>` in the
563
+ * tree was configured with a matching `defaultConfig: TConfig`. If you pass a
564
+ * wider `TConfig` than what the provider actually carries, field access at
565
+ * compile time will look fine but resolve to `undefined` at runtime.
566
+ *
567
+ * The intended pattern is that extension packages (e.g. `@ai-react-markdown/mantine`)
568
+ * ship their own narrow wrapper hook alongside a matching `defaultConfig`, so the
569
+ * assertion is made *once* next to the provider configuration and consumers of the
570
+ * wrapper never touch the raw generic.
571
+ *
572
+ * @typeParam TConfig - Caller-asserted configuration shape (defaults to
573
+ * {@link AIMarkdownRenderConfig}). Must be aligned with the provider's
574
+ * `defaultConfig` — the library does not check this at runtime.
575
+ * @returns The current render state (does not include metadata — use
576
+ * {@link useAIMarkdownMetadata} for that).
577
+ * @throws If called outside an `<AIMarkdown>` provider tree.
578
+ *
579
+ * @example Base usage — no generic, always safe:
501
580
  * ```tsx
502
581
  * function CustomCodeBlock({ children }: PropsWithChildren) {
503
582
  * const { streaming, config } = useAIMarkdownRenderState();
504
- * // ...
583
+ * // config: AIMarkdownRenderConfig — guaranteed shape
584
+ * }
585
+ * ```
586
+ *
587
+ * @example Wrapper-hook pattern — the intended way to use an extended TConfig:
588
+ * ```tsx
589
+ * // In your extension package (pin the assertion in one place):
590
+ * interface ExtendedConfig extends AIMarkdownRenderConfig {
591
+ * themeMode: 'light' | 'dark' | 'auto';
505
592
  * }
593
+ * export const extendedDefaultConfig: ExtendedConfig = {
594
+ * ...defaultAIMarkdownRenderConfig,
595
+ * themeMode: 'auto',
596
+ * };
597
+ * export const useExtendedRenderState = () =>
598
+ * useAIMarkdownRenderState<ExtendedConfig>();
599
+ *
600
+ * // Provider is always configured with the matching defaultConfig:
601
+ * <AIMarkdown defaultConfig={extendedDefaultConfig} ...>{children}</AIMarkdown>
602
+ *
603
+ * // Consumers use the narrow wrapper — no raw generic anywhere:
604
+ * const { config } = useExtendedRenderState();
605
+ * config.themeMode; // correctly typed and present at runtime
506
606
  * ```
607
+ *
608
+ * @see `@ai-react-markdown/mantine` — real-world reference. Its
609
+ * `MantineAIMarkdownRenderConfig`, `defaultMantineAIMarkdownRenderConfig`,
610
+ * `<MantineAIMarkdown>` (which passes `defaultConfig` by default), and
611
+ * `useMantineAIMarkdownRenderState` implement this exact pattern.
507
612
  */
508
613
  declare function useAIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>(): AIMarkdownRenderState<TConfig>;
509
614
  /**
@@ -513,8 +618,24 @@ declare function useAIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig
513
618
  * do not cause re-renders in components that only consume render state
514
619
  * (e.g. {@link MarkdownContent}).
515
620
  *
516
- * @typeParam TMetadata - Expected metadata shape (defaults to {@link AIMarkdownMetadata}).
621
+ * ### `TMetadata` is a caller-asserted type
622
+ *
623
+ * Same contract as {@link useAIMarkdownRenderState} — the generic is an
624
+ * assertion about the `metadata` prop passed to the provider above, not a
625
+ * value TypeScript can derive. Unlike render-state config, metadata has no
626
+ * runtime fallback: if the provider received no `metadata`, the hook returns
627
+ * `undefined` regardless of the asserted type. Prefer wrapping this hook in
628
+ * a project-local hook that pins `TMetadata` next to the call site that
629
+ * actually provides the metadata.
630
+ *
631
+ * @typeParam TMetadata - Caller-asserted metadata shape (defaults to
632
+ * {@link AIMarkdownMetadata}). Caller is responsible for ensuring the
633
+ * provider's `metadata` prop matches this shape.
517
634
  * @returns The current metadata, or `undefined` if none was provided.
635
+ *
636
+ * @see `@ai-react-markdown/mantine` — `useMantineAIMarkdownMetadata` applies
637
+ * the wrapper pattern to this hook, pinning `MantineAIMarkdownMetadata` in
638
+ * a single location.
518
639
  */
519
640
  declare function useAIMarkdownMetadata<TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata>(): TMetadata | undefined;
520
641
  /** Props for {@link AIMarkdownRenderStateProvider}. */
@@ -523,6 +644,19 @@ interface AIMarkdownRenderStateProviderProps<TConfig extends AIMarkdownRenderCon
523
644
  fontSize: string;
524
645
  variant: AIMarkdownVariant;
525
646
  colorScheme: AIMarkdownColorScheme;
647
+ /**
648
+ * Logical-document identifier used as the id namespace for clobberable
649
+ * attributes (id / hash hrefs). Optional — when omitted, the provider
650
+ * auto-generates one via {@link useId} so the provider stays drop-in
651
+ * usable for direct consumers (e.g. extension packages that don't go
652
+ * through `<AIMarkdown>`).
653
+ *
654
+ * Pass the SAME value to multiple providers / `<AIMarkdown>` instances
655
+ * when they render chunks of the same logical document — their id
656
+ * prefixes will align so cross-chunk anchors and (once the parser sees
657
+ * the full doc) footnote navigation work.
658
+ */
659
+ documentId?: string;
526
660
  /**
527
661
  * Base default config to merge against. When omitted, falls back to
528
662
  * {@link defaultAIMarkdownRenderConfig}. Sub-packages (e.g. mantine) can
@@ -572,6 +706,14 @@ type AIMDContentPreprocessor = (content: string) => string;
572
706
  * returned, preventing unnecessary re-renders in downstream `useMemo` / `useEffect`
573
707
  * consumers that depend on reference equality.
574
708
  *
709
+ * The ref is updated in a layout effect (not during render) so that the cached
710
+ * reference only advances on COMMITTED renders. In concurrent mode a render
711
+ * may be discarded (e.g. by Suspense); writing to the ref during render would
712
+ * let values from discarded renders pollute the cache and leak into subsequent
713
+ * committed renders. Layout effects run synchronously right after commit, which
714
+ * closes the window where a same-tick re-render could otherwise observe a stale
715
+ * `ref.current` and hand back an outdated reference.
716
+ *
575
717
  * @typeParam T - The value type.
576
718
  * @param value - The potentially new value to stabilize.
577
719
  * @returns The previous reference when deep-equal, otherwise the new value.
@@ -632,12 +774,34 @@ interface AIMarkdownProps<TConfig extends AIMarkdownRenderConfig = AIMarkdownRen
632
774
  variant?: AIMarkdownVariant;
633
775
  /** Color scheme name. Defaults to `'light'`. */
634
776
  colorScheme?: AIMarkdownColorScheme;
777
+ /**
778
+ * Stable identifier for the *logical markdown document* this `<AIMarkdown>`
779
+ * is rendering. Used as the id namespace for all clobberable attributes
780
+ * (`id`, hash hrefs) so two documents on the same page do not cross-link —
781
+ * e.g. clicking a footnote `[^1]` in message A will not scroll to the
782
+ * `[^1]` definition in message B.
783
+ *
784
+ * Why `documentId` and not `instanceId`: when one logical document is
785
+ * split across multiple `<AIMarkdown>` instances (chunked / streamed
786
+ * rendering), every chunk should share the SAME `documentId` so their
787
+ * id-prefixes line up. The id is per-document, not per-React-instance.
788
+ *
789
+ * When omitted, an id is auto-generated via React's `useId()` (SSR-safe
790
+ * and stable across re-renders). Pass an explicit value when you need
791
+ * deterministic ids (snapshot tests, cross-component deep links) or when
792
+ * multiple instances render the same logical document.
793
+ *
794
+ * Consumer-supplied values pass through `encodeURIComponent` at the prefix
795
+ * construction site, so any string is safe — including ids with reserved
796
+ * characters like `:`, `/`, or spaces.
797
+ */
798
+ documentId?: string;
635
799
  }
636
800
  /**
637
801
  * Root component that preprocesses markdown content and renders it through
638
802
  * a configurable remark/rehype pipeline wrapped in typography and style layers.
639
803
  */
640
- declare const AIMarkdownComponent: <TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig, TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata>({ streaming, content, fontSize, contentPreprocessors, customComponents, defaultConfig, config, metadata, Typography, ExtraStyles, variant, colorScheme, }: AIMarkdownProps<TConfig, TRenderData>) => react_jsx_runtime.JSX.Element;
804
+ declare const AIMarkdownComponent: <TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig, TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata>({ streaming, content, fontSize, contentPreprocessors, customComponents, defaultConfig, config, metadata, Typography, ExtraStyles, variant, colorScheme, documentId, }: AIMarkdownProps<TConfig, TRenderData>) => react_jsx_runtime.JSX.Element;
641
805
  declare const _default: typeof AIMarkdownComponent;
642
806
 
643
807
  export { type AIMDContentPreprocessor, type AIMarkdownColorScheme, type AIMarkdownCustomComponents, type AIMarkdownExtraStylesComponent, type AIMarkdownExtraStylesProps, type AIMarkdownMetadata, type AIMarkdownProps, type AIMarkdownRenderConfig, AIMarkdownRenderDisplayOptimizeAbility, AIMarkdownRenderExtraSyntax, type AIMarkdownRenderState, type AIMarkdownTypographyComponent, type AIMarkdownTypographyProps, type AIMarkdownVariant, type PartialDeep, _default as default, defaultAIMarkdownRenderConfig, useAIMarkdownMetadata, useAIMarkdownRenderState, useStableValue };