instui-markdown 0.0.8 → 0.0.9

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.
Files changed (3) hide show
  1. package/README.md +38 -5
  2. package/dist/index.mjs +85 -28
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -120,16 +120,49 @@ Wraps color literals in a visual swatch. Supports `#hex`, `rgb()`, `rgba()`, `hs
120
120
 
121
121
  ### `icons`
122
122
 
123
- Renders InstUI icons from `:iconName:` tokens. Supports Line, Solid, and Lucide icon variants. You can omit the `Icon` prefix and the `Line`/`Solid` suffix `:Heart:`, `:HeartLine:`, and `:IconHeartLine:` all resolve to the same icon.
123
+ Renders icon tokens from `:iconName:` syntax. InstUI lookup runs first, then optional SimpleIcons lookup can run as a fallback through an app-provided resolver. You can omit the `Icon` prefix and the `Line`/`Solid` suffix for InstUI names, so `:Heart:`, `:HeartLine:`, and `:IconHeartLine:` all resolve to the same InstUI icon.
124
124
 
125
125
  Add an optional hex color with a pipe: `:Heart|#F00:`.
126
126
 
127
127
  Skips icon tokens inside code fences.
128
128
 
129
- | Option | Type | Default | Description |
130
- | --------- | --------- | ----------- | -------------------------------------------- |
131
- | `enabled` | `boolean` | `false` | Renders `:icon:` tokens as InstUI icons |
132
- | `color` | `string` | `undefined` | Default icon color (hex) for all icon tokens |
129
+ | Option | Type | Default | Description |
130
+ | ----------------------- | --------------------------------------- | ----------- | ------------------------------------------------------------------ |
131
+ | `enabled` | `boolean` | `false` | Enables token-based icon rendering |
132
+ | `color` | `string` | `undefined` | Default color for all icon providers |
133
+ | `providers.instui` | `boolean` | `true` | Enables InstUI lookup (`@instructure/ui-icons`) |
134
+ | `providers.simpleIcons` | `boolean` | `true` | Enables SimpleIcons fallback lookup |
135
+ | `simpleIcons.color` | `string` | `undefined` | Default color for SimpleIcons output (falls back to `icons.color`) |
136
+ | `simpleIcons.resolve` | `(code: string) => SimpleIconTokenData` | `undefined` | App-provided resolver for SimpleIcons SVG path data |
137
+
138
+ Example with SimpleIcons fallback:
139
+
140
+ ```tsx
141
+ <InstuiMarkdown
142
+ renderOptions={{
143
+ icons: {
144
+ enabled: true,
145
+ providers: { instui: true, simpleIcons: true },
146
+ simpleIcons: {
147
+ resolve: (code) => {
148
+ if (code.toLowerCase() === "github") {
149
+ return {
150
+ title: "GitHub",
151
+ path: "M12 0C5.37 0 0 5.37 0 12...",
152
+ viewBox: "0 0 24 24",
153
+ };
154
+ }
155
+ return undefined;
156
+ },
157
+ },
158
+ },
159
+ }}
160
+ >
161
+ {":GitHub: and :HeartLine:"}
162
+ </InstuiMarkdown>
163
+ ```
164
+
165
+ If no enabled provider can resolve a token, the original token text is left in place.
133
166
 
134
167
  ## Peer dependencies
135
168
 
package/dist/index.mjs CHANGED
@@ -442,41 +442,90 @@ function createBlockquoteComponent(options) {
442
442
  }
443
443
  //#endregion
444
444
  //#region src/components/span-component.tsx
445
+ function toPascalCase(str) {
446
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
447
+ }
448
+ function parseIconTokenName(iconName) {
449
+ const withoutPrefix = iconName.startsWith("Icon") ? iconName.slice(4) : iconName;
450
+ const isSolid = withoutPrefix.endsWith("Solid");
451
+ const isInstUI = withoutPrefix.endsWith("InstUIIcon");
452
+ let root = withoutPrefix;
453
+ if (isSolid) root = withoutPrefix.slice(0, -5);
454
+ else if (isInstUI) root = withoutPrefix.slice(0, -10);
455
+ else if (withoutPrefix.endsWith("Line")) root = withoutPrefix.slice(0, -4);
456
+ return {
457
+ withoutPrefix,
458
+ isSolid,
459
+ isInstUI,
460
+ root
461
+ };
462
+ }
463
+ function getSimpleIconCodes(iconName, root, withoutPrefix) {
464
+ const camel = root.charAt(0).toLowerCase() + root.slice(1);
465
+ const lower = root.toLowerCase();
466
+ return Array.from(new Set([
467
+ iconName,
468
+ withoutPrefix,
469
+ root,
470
+ camel,
471
+ lower
472
+ ]));
473
+ }
445
474
  function createSpanComponent(options) {
446
475
  return ({ children, className, ...props }) => {
447
476
  if (className?.includes("icon-token") && options.showIcons) {
448
477
  const iconName = props["data-icon"];
449
478
  const inlineIconColor = props["data-icon-color"];
450
479
  if (iconName) {
451
- const withoutPrefix = iconName.startsWith("Icon") ? iconName.slice(4) : iconName;
452
- const isSolid = withoutPrefix.endsWith("Solid");
453
- const isInstUI = withoutPrefix.endsWith("InstUIIcon");
454
- let root = withoutPrefix;
455
- if (isSolid) root = withoutPrefix.slice(0, -5);
456
- else if (isInstUI) root = withoutPrefix.slice(0, -10);
457
- else if (withoutPrefix.endsWith("Line")) root = withoutPrefix.slice(0, -4);
458
- const IconComponent = (isSolid ? [`Icon${root}Solid`] : isInstUI ? [`${root}InstUIIcon`] : [
459
- `${root}InstUIIcon`,
460
- `Icon${root}Line`,
461
- `Icon${root}`
462
- ]).map((name) => InstUIIcons[name]).find(Boolean);
463
- if (IconComponent) {
464
- const resolvedColor = inlineIconColor ?? options.iconColor;
465
- const normalizedIconColor = resolvedColor && isLiteralColorValue(resolvedColor) ? normalizeColorForSwatch(resolvedColor) : void 0;
466
- const sizeProps = IconComponent.displayName?.startsWith("InstUIIcon_") ? { size: "x-small" } : {};
467
- if (normalizedIconColor) return /* @__PURE__ */ jsx("span", {
468
- style: { color: normalizedIconColor },
469
- children: /* @__PURE__ */ jsx(IconComponent, {
480
+ const { withoutPrefix, isSolid, isInstUI, root } = parseIconTokenName(iconName);
481
+ if (options.enableInstuiIcons) {
482
+ const normalizedRoot = toPascalCase(root);
483
+ const IconComponent = (isSolid ? [`Icon${normalizedRoot}Solid`] : isInstUI ? [`${normalizedRoot}InstUIIcon`] : [
484
+ `${normalizedRoot}InstUIIcon`,
485
+ `Icon${normalizedRoot}Line`,
486
+ `Icon${normalizedRoot}`
487
+ ]).map((name) => InstUIIcons[name]).find(Boolean);
488
+ if (IconComponent) {
489
+ const resolvedColor = inlineIconColor ?? options.iconColor;
490
+ const normalizedIconColor = resolvedColor && isLiteralColorValue(resolvedColor) ? normalizeColorForSwatch(resolvedColor) : void 0;
491
+ const sizeProps = IconComponent.displayName?.startsWith("InstUIIcon_") ? { size: "x-small" } : {};
492
+ if (normalizedIconColor) return /* @__PURE__ */ jsx("span", {
493
+ style: { color: normalizedIconColor },
494
+ children: /* @__PURE__ */ jsx(IconComponent, {
495
+ title: iconName,
496
+ color: "inherit",
497
+ ...sizeProps
498
+ })
499
+ });
500
+ return /* @__PURE__ */ jsx(IconComponent, {
470
501
  title: iconName,
471
- color: "inherit",
502
+ color: (resolvedColor === "currentColor" ? "inherit" : resolvedColor) ?? "inherit",
472
503
  ...sizeProps
473
- })
474
- });
475
- return /* @__PURE__ */ jsx(IconComponent, {
476
- title: iconName,
477
- color: (resolvedColor === "currentColor" ? "inherit" : resolvedColor) ?? "inherit",
478
- ...sizeProps
479
- });
504
+ });
505
+ }
506
+ }
507
+ if (options.enableSimpleIcons && options.resolveSimpleIcon) {
508
+ const simpleIcon = getSimpleIconCodes(iconName, root, withoutPrefix).map((code) => options.resolveSimpleIcon?.(code)).find(Boolean);
509
+ if (simpleIcon?.path) {
510
+ const resolvedColor = inlineIconColor ?? options.simpleIconColor ?? options.iconColor;
511
+ const normalizedIconColor = resolvedColor && isLiteralColorValue(resolvedColor) ? normalizeColorForSwatch(resolvedColor) : void 0;
512
+ const label = simpleIcon.title ?? root;
513
+ return /* @__PURE__ */ jsx("span", {
514
+ style: normalizedIconColor ? { color: normalizedIconColor } : void 0,
515
+ children: /* @__PURE__ */ jsxs(InlineSVG, {
516
+ inline: false,
517
+ viewBox: simpleIcon.viewBox ?? "0 0 24 24",
518
+ width: simpleIcon.width ?? "1em",
519
+ height: simpleIcon.height ?? "1em",
520
+ role: "img",
521
+ "aria-label": label,
522
+ children: [/* @__PURE__ */ jsx("title", { children: label }), /* @__PURE__ */ jsx("path", {
523
+ d: simpleIcon.path,
524
+ fill: "currentColor"
525
+ })]
526
+ })
527
+ });
528
+ }
480
529
  }
481
530
  }
482
531
  }
@@ -654,11 +703,19 @@ function createInstuiMarkdownComponents(renderOptions = {}) {
654
703
  const showColorCodes = Boolean(renderOptions.color?.enabled);
655
704
  const showIcons = Boolean(renderOptions.icons?.enabled);
656
705
  const iconColor = renderOptions.icons?.color;
706
+ const enableInstuiIcons = renderOptions.icons?.providers?.instui ?? true;
707
+ const enableSimpleIcons = renderOptions.icons?.providers?.simpleIcons ?? true;
708
+ const simpleIconColor = renderOptions.icons?.simpleIcons?.color;
709
+ const resolveSimpleIcon = renderOptions.icons?.simpleIcons?.resolve;
657
710
  return {
658
711
  span: createSpanComponent({
659
712
  showColorCodes,
660
713
  showIcons,
661
- iconColor
714
+ iconColor,
715
+ enableInstuiIcons,
716
+ enableSimpleIcons,
717
+ simpleIconColor,
718
+ resolveSimpleIcon
662
719
  }),
663
720
  a: createLinkComponent({
664
721
  showExternalIcon,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instui-markdown",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "InstUI markdown renderer components",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -21,7 +21,7 @@
21
21
  "access": "public"
22
22
  },
23
23
  "dependencies": {
24
- "rehype-instui-markdown": "0.0.7"
24
+ "rehype-instui-markdown": "0.0.9"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^24",