@janga/norna 0.7.28 → 0.7.29

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 (77) hide show
  1. package/README.md +8 -5
  2. package/astro.config.mjs +14 -2
  3. package/package.json +10 -8
  4. package/schemas/config.schema.json +80 -65
  5. package/schemas/content-frontmatter.schema.json +15 -8
  6. package/schemas/manifest.json +1 -1
  7. package/schemas/page-theme.schema.json +237 -237
  8. package/schemas/root-theme.schema.json +237 -237
  9. package/schemas/sitewide-content.schema.json +18 -18
  10. package/schemas/theme.schema.json +237 -237
  11. package/scripts/add-site-node.mjs +29 -100
  12. package/scripts/dev-local.mjs +34 -7
  13. package/scripts/generate-images.mjs +83 -65
  14. package/scripts/generate-search-index.mjs +3 -2
  15. package/scripts/lib/attachment-render.mjs +23 -0
  16. package/scripts/lib/dev-server-identity.mjs +30 -0
  17. package/scripts/lib/documentation-routes.json +63 -62
  18. package/scripts/lib/docusaurus-migration-audit.mjs +9 -9
  19. package/scripts/lib/editor-attachments.mjs +69 -0
  20. package/scripts/lib/editor-file-policy.mjs +2 -0
  21. package/scripts/lib/editor-language-service.mjs +21 -18
  22. package/scripts/lib/editor-page-addresses.mjs +5 -3
  23. package/scripts/lib/editor-page-placement.mjs +28 -98
  24. package/scripts/lib/editor-resource-actions.mjs +216 -0
  25. package/scripts/lib/editor-site-editing-tree.mjs +44 -9
  26. package/scripts/lib/editor-site-files.mjs +3 -2
  27. package/scripts/lib/editor-site-links.mjs +6 -4
  28. package/scripts/lib/editor-site-preview.mjs +149 -0
  29. package/scripts/lib/editor-site-tree.mjs +10 -3
  30. package/scripts/lib/editor-source-files.mjs +1 -1
  31. package/scripts/lib/logo-assets.mjs +2 -1
  32. package/scripts/lib/markdown-images.mjs +59 -0
  33. package/scripts/lib/markdown-links.mjs +29 -9
  34. package/scripts/lib/norna-markdown-blocks.mjs +0 -57
  35. package/scripts/lib/norna-markdown-render-plugin.mjs +29 -0
  36. package/scripts/lib/page-aliases.mjs +5 -0
  37. package/scripts/lib/page-attachments.mjs +59 -0
  38. package/scripts/lib/page-markdown.mjs +11 -7
  39. package/scripts/lib/page-model.mjs +8 -22
  40. package/scripts/lib/page-move-apply.mjs +6 -4
  41. package/scripts/lib/page-move-plan.mjs +37 -19
  42. package/scripts/lib/page-order-source.mjs +22 -0
  43. package/scripts/lib/page-order.mjs +45 -0
  44. package/scripts/lib/page-placement-cli.mjs +50 -0
  45. package/scripts/lib/project-config.mjs +3 -0
  46. package/scripts/lib/publication-paths.mjs +72 -0
  47. package/scripts/lib/schema-definitions.mjs +4 -0
  48. package/scripts/lib/schema-editor-metadata.mjs +4 -1
  49. package/scripts/lib/site-content.mjs +1 -1
  50. package/scripts/lib/site-link-graph.mjs +88 -18
  51. package/scripts/lib/site-node-create.mjs +47 -34
  52. package/scripts/lib/site-paths.mjs +2 -2
  53. package/scripts/lib/site-source-format.mjs +12 -0
  54. package/scripts/lib/site-structure.mjs +14 -13
  55. package/scripts/lib/site-upgrade.mjs +2 -0
  56. package/scripts/lib/social-image-assets.mjs +2 -1
  57. package/scripts/lib/source-files.mjs +1 -1
  58. package/scripts/lib/structured-blocks.mjs +3 -1
  59. package/scripts/move-site-page.mjs +46 -133
  60. package/scripts/sync-content-sections.mjs +8 -10
  61. package/scripts/sync-site-public.mjs +44 -66
  62. package/src/components/ContentBlocks.astro +2 -1
  63. package/src/components/ImageStack.astro +7 -5
  64. package/src/components/SearchPage.astro +2 -1
  65. package/src/components/SitePage.astro +1 -0
  66. package/src/components/SiteSection.astro +1 -0
  67. package/src/content.config.ts +1 -4
  68. package/src/lib/imagePresentation.ts +3 -0
  69. package/src/lib/markdownImages.ts +101 -0
  70. package/src/lib/sectionContent.ts +64 -19
  71. package/src/lib/sitePages.ts +4 -30
  72. package/src/lib/sitePublicAssets.ts +2 -1
  73. package/src/pages/[...slug].astro +2 -1
  74. package/src/styles/area-navigation.css +14 -5
  75. package/src/styles/content.css +6 -0
  76. package/src/styles/media.css +24 -0
  77. package/starters/basic/site/site-config/settings.yaml +1 -0
@@ -1,5 +1,7 @@
1
1
  import { getPageImageSourceKey } from '../../scripts/lib/site-conventions.mjs';
2
+ import { readPageAttachments, resolveAttachment, attachmentHref } from '../../scripts/lib/page-attachments.mjs';
2
3
  import { markdownToHtml } from 'satteri';
4
+ import { parseFragment } from 'parse5';
3
5
  import { groupRenderedTabs } from '../../scripts/lib/content-tabs.mjs';
4
6
  import projectConfig from '../../scripts/lib/project-config.mjs';
5
7
  import {
@@ -8,12 +10,20 @@ import {
8
10
  import { splitNornaRenderedBlocks } from '../../scripts/lib/norna-markdown-blocks.mjs';
9
11
  import { applyBasePathToHtml } from './basePath';
10
12
  import type { SitePage } from './sitePages';
13
+ import { splitMarkdownImages, renderNestedMarkdownImages } from './markdownImages';
14
+ import { nornaMarkdownImagePlugin } from '../../scripts/lib/norna-markdown-render-plugin.mjs';
15
+ import { rewriteAttachmentLinks } from '../../scripts/lib/attachment-render.mjs';
16
+ import { siteDir } from '../../scripts/lib/site-paths.mjs';
17
+ import { pathToFileURL } from 'node:url';
11
18
 
12
19
  type ManagedImage = {
13
20
  image: string;
14
21
  src: string;
15
22
  alt?: string;
16
23
  caption?: string;
24
+ title?: string;
25
+ href?: string;
26
+ linkTitle?: string;
17
27
  };
18
28
  type CardListItem = {
19
29
  title: string;
@@ -94,40 +104,73 @@ const applyH3HeadingIds = (
94
104
  html: string,
95
105
  headings: Array<{ id: string | null }>,
96
106
  ) => {
107
+ const renderHeading = (attributes: string, headingHtml: string, heading: { id: string | null }) => {
108
+ if (!heading.id) throw new Error('Rendered Markdown H3 could not be matched to its heading id.');
109
+ const cleanAttributes = attributes
110
+ .replace(/\s+data-norna-page-heading="true"/g, '')
111
+ .replace(/\s+id=(["']).*?\1/gi, '');
112
+ const cleanHeadingHtml = headingHtml.replace(explicitHeadingIdRegex, '').trim();
113
+ return `<h3${cleanAttributes} id="${heading.id}">${cleanHeadingHtml}</h3>`;
114
+ };
115
+
116
+ // Content rendered before the heading marker was introduced may still be in
117
+ // Astro's content cache. Its top-level H3s can be identified from the HTML tree.
118
+ if (!html.includes('data-norna-page-heading=') && headings.length > 0) {
119
+ const fragment = parseFragment(html, { sourceCodeLocationInfo: true });
120
+ const renderedHeadings = fragment.childNodes.filter((node) => (
121
+ 'tagName' in node && node.tagName === 'h3' && node.sourceCodeLocation?.startTag && node.sourceCodeLocation?.endTag
122
+ ));
123
+ if (renderedHeadings.length !== headings.length) {
124
+ throw new Error('Markdown H3 headings could not be matched to the rendered page content.');
125
+ }
126
+ let result = html;
127
+ for (let index = renderedHeadings.length - 1; index >= 0; index -= 1) {
128
+ const location = renderedHeadings[index]?.sourceCodeLocation;
129
+ const heading = headings[index];
130
+ if (!location?.startTag || !location.endTag || !heading) {
131
+ throw new Error('Rendered Markdown H3 could not be matched to its heading id.');
132
+ }
133
+ const source = html.slice(location.startOffset, location.endOffset);
134
+ const replacement = source.replace(/<h3\b([^>]*)>([\s\S]*?)<\/h3>/i, (_match, attributes, headingHtml) =>
135
+ renderHeading(String(attributes), String(headingHtml), heading));
136
+ result = result.slice(0, location.startOffset) + replacement + result.slice(location.endOffset);
137
+ }
138
+ return result;
139
+ }
140
+
97
141
  let headingIndex = 0;
98
- const result = html.replace(/<h3\b([^>]*)>([\s\S]*?)<\/h3>/gi, (_match, attributes, headingHtml) => {
142
+ const result = html.replace(/<h3\b([^>]*)>([\s\S]*?)<\/h3>/gi, (source, attributes, headingHtml) => {
143
+ if (!/\sdata-norna-page-heading="true"/.test(attributes)) return source;
99
144
  const heading = headings[headingIndex];
100
145
  headingIndex += 1;
101
- if (!heading?.id) throw new Error('Rendered Markdown H3 could not be matched to its heading id.');
102
-
103
- const cleanAttributes = String(attributes).replace(/\s+id=(["']).*?\1/gi, '');
104
- const cleanHeadingHtml = String(headingHtml).replace(explicitHeadingIdRegex, '').trim();
105
- return `<h3${cleanAttributes} id="${heading.id}">${cleanHeadingHtml}</h3>`;
146
+ if (!heading) throw new Error('Rendered Markdown H3 could not be matched to its heading id.');
147
+ return renderHeading(String(attributes), String(headingHtml), heading);
106
148
  });
107
-
108
149
  if (headingIndex !== headings.length) {
109
150
  throw new Error('Markdown H3 headings could not be matched to the rendered page content.');
110
151
  }
111
-
112
152
  return result;
113
153
  };
114
154
 
115
155
  const getImageSourceKey = (page: SitePage, image: string) => getPageImageSourceKey(page.pageDirectory, image);
116
156
 
117
- const renderHeadingTitleHtml = async (headingSource: string) => {
118
- const headingMarkdown = headingSource
119
- .replace(/^#{1,6}[ \t]+/, '')
120
- .replace(explicitHeadingIdRegex, '')
121
- .trim();
122
- const result = await markdownToHtml(headingMarkdown, {
157
+ const renderHeadingTitleHtml = async (headingSource: string, page: SitePage) => {
158
+ const references = [...page.markdownDocument.markdownImages, ...page.markdownDocument.links];
159
+ const definitions = [...new Set(references.map((reference) => reference.definitionSource).filter(Boolean))].join('\n');
160
+ // Keep the heading syntax: text such as "1. Plan" becomes a list if parsed alone.
161
+ const source = await rewriteAttachmentLinks(`${headingSource}\n\n${definitions}`, pathToFileURL(page.contentPath), siteDir);
162
+ const result = await markdownToHtml(source, {
163
+ mdastPlugins: [nornaMarkdownImagePlugin],
123
164
  features: {
124
165
  gfm: true,
125
166
  smartPunctuation: true,
126
167
  },
127
168
  });
128
- const paragraph = result.html.trim().match(/^<p>([\s\S]*)<\/p>$/i);
129
- if (!paragraph) throw new Error('Markdown heading could not be rendered as inline content.');
130
- return prepareContentHtml(paragraph[1] ?? '');
169
+ const heading = result.html.trim().match(/^<h([12])\b[^>]*>([\s\S]*)<\/h\1>$/i);
170
+ if (!heading) throw new Error('Markdown heading could not be rendered as inline content.');
171
+ const html = prepareContentHtml((heading[2] ?? '').replace(explicitHeadingIdRegex, '').trim());
172
+ const marked = splitMarkdownImages(html, page.pageDirectory).map((block) => block.type === 'html' ? block.html : '').join('');
173
+ return renderNestedMarkdownImages(marked, page.title, { desktop: 100, mobile: 100 });
131
174
  };
132
175
 
133
176
  const splitRenderedRegions = (html: string, regionCount: number) => {
@@ -159,11 +202,12 @@ const resolveContentBlocks = async (
159
202
 
160
203
  for (const block of splitBlocks) {
161
204
  if (block.type === 'html') {
162
- resolvedBlocks.push({ type: 'html', html: prepareContentHtml(block.html) });
205
+ resolvedBlocks.push(...splitMarkdownImages(prepareContentHtml(block.html), page.pageDirectory));
163
206
  continue;
164
207
  }
165
208
 
166
209
  if (block.type === 'card-list') {
210
+ const { files } = await readPageAttachments(page);
167
211
  resolvedBlocks.push({
168
212
  type: 'card-list',
169
213
  layout: block.layout,
@@ -172,6 +216,7 @@ const resolveContentBlocks = async (
172
216
  width: block.width,
173
217
  cards: block.cards.map((card: CardListItem) => ({
174
218
  ...card,
219
+ ...(card.link && resolveAttachment(card.link, page.pathname, files) ? { link: attachmentHref(resolveAttachment(card.link, page.pathname, files), card.link) } : {}),
175
220
  ...(card.image ? { src: getImageSourceKey(page, card.image) } : {}),
176
221
  })),
177
222
  });
@@ -258,7 +303,7 @@ export const getSectionsContent = async (
258
303
  sections.push({
259
304
  id,
260
305
  title,
261
- titleHtml: await renderHeadingTitleHtml(bodySection.heading.source),
306
+ titleHtml: await renderHeadingTitleHtml(bodySection.heading.source, page),
262
307
  headingLevel,
263
308
  contentBlocks: groupRenderedTabs(
264
309
  await resolveContentBlocks(content, bodySection.blocks, page, childPages),
@@ -16,7 +16,7 @@ type SiteNodeBase = {
16
16
  isHome: boolean;
17
17
  navigation: {
18
18
  listed: boolean;
19
- order: number;
19
+ order: number | null;
20
20
  };
21
21
  depth: number;
22
22
  pageDirectories: string[];
@@ -25,7 +25,6 @@ type SiteNodeBase = {
25
25
  pageDirectory: string;
26
26
  pageId: string;
27
27
  pageIds: string[];
28
- pageOrders: number[];
29
28
  pagePath: string;
30
29
  parentPagePath: string | null;
31
30
  title: string;
@@ -64,16 +63,6 @@ const getPageMetadata = (entry: SiteEntry) => {
64
63
 
65
64
  const getPagePathname = (pathSegment: string) => pathSegment ? `/${pathSegment}/` : '/';
66
65
 
67
- const compareNumberPaths = (left: number[], right: number[]) => {
68
- const sharedLength = Math.min(left.length, right.length);
69
- for (let index = 0; index < sharedLength; index += 1) {
70
- const difference = left[index] - right[index];
71
- if (difference !== 0) return difference;
72
- }
73
-
74
- return left.length - right.length;
75
- };
76
-
77
66
  const readPageMarkdownDocument = async (entry: SiteEntry) => {
78
67
  const pageDirectory = getPageDirectory(entry);
79
68
  const contentRelativePath = [getPageSourceDirectory(pageDirectory), 'content.md'].filter(Boolean).join('/');
@@ -112,7 +101,7 @@ const createSitePage = async (entry: SiteEntry): Promise<SitePage> => {
112
101
  depth: pageMetadata.depth,
113
102
  navigation: {
114
103
  listed: navigation.listed ?? true,
115
- order: isHome ? 0 : pageMetadata.pageOrder,
104
+ order: isHome ? 0 : navigation.order ?? null,
116
105
  },
117
106
  pathSegment,
118
107
  pathname: getPagePathname(pathSegment),
@@ -120,7 +109,6 @@ const createSitePage = async (entry: SiteEntry): Promise<SitePage> => {
120
109
  pageDirectories: pageMetadata.pageDirectories,
121
110
  pageId,
122
111
  pageIds: pageMetadata.pageIds,
123
- pageOrders: pageMetadata.pageOrders,
124
112
  pagePath,
125
113
  parentPagePath: isHome ? null : pageMetadata.parentPagePath,
126
114
  contentLabel,
@@ -148,7 +136,6 @@ const createSiteCategory = (category: Awaited<ReturnType<typeof getSiteStructure
148
136
  pageDirectories: category.pageDirectories,
149
137
  pageId: category.pageId,
150
138
  pageIds: category.pageIds,
151
- pageOrders: category.pageOrders,
152
139
  pagePath: category.pagePath,
153
140
  parentPagePath: category.parentPagePath,
154
141
  title: category.label,
@@ -160,7 +147,6 @@ const createSitePages = async (entries: SiteEntry[]) => {
160
147
  const pages = await Promise.all(entries.map(createSitePage));
161
148
  const pathnames = new Map<string, SitePage>();
162
149
  const siblingPageIds = new Map<string, SitePage>();
163
- const siblingPageOrders = new Map<string, SitePage>();
164
150
 
165
151
  for (const page of pages) {
166
152
  if (page.isHome && page.navigation.listed === false) {
@@ -184,24 +170,12 @@ const createSitePages = async (entries: SiteEntry[]) => {
184
170
 
185
171
  siblingPageIds.set(pageIdKey, page);
186
172
 
187
- const pageOrderKey = `${siblingScope}\0${page.navigation.order}`;
188
- const existingPageOrder = siblingPageOrders.get(pageOrderKey);
189
- if (existingPageOrder) {
190
- throw new Error(`Duplicate sibling page order "${String(page.navigation.order).padStart(3, '0')}" below "${page.parentPagePath ?? '/'}" from "${existingPageOrder.entry.id}" and "${page.entry.id}".`);
191
- }
192
-
193
- siblingPageOrders.set(pageOrderKey, page);
194
173
  }
195
174
  if (!pages.some(({ isHome }) => isHome)) {
196
175
  throw new Error(`Homepage content ${siteDirLabel}/root/content.md is missing.`);
197
176
  }
198
177
 
199
- return pages.sort((left, right) => (
200
- left.isHome ? -1 : right.isHome ? 1 :
201
- compareNumberPaths(left.pageOrders, right.pageOrders) ||
202
- left.title.localeCompare(right.title, 'sv') ||
203
- left.pathname.localeCompare(right.pathname, 'sv')
204
- ));
178
+ return pages;
205
179
  };
206
180
 
207
181
  export const getSiteModel = async (entries: SiteEntry[]) => {
@@ -228,7 +202,7 @@ export const getSiteModel = async (entries: SiteEntry[]) => {
228
202
  categoryDestinations: assertCategoryDestinationModel(createCategoryDestinationModel(nodes)),
229
203
  categories: nodes.filter((node): node is SiteCategory => node.kind === 'category'),
230
204
  nodes,
231
- pages,
205
+ pages: nodes.filter((node): node is SitePage => node.kind === 'page'),
232
206
  warnings: structure.warnings,
233
207
  };
234
208
  };
@@ -1,3 +1,4 @@
1
+ import { publicFilePublication } from '../../scripts/lib/publication-paths.mjs';
1
2
  import { existsSync } from 'node:fs';
2
3
  import path from 'node:path';
3
4
  import { sitePublicDir } from '../../scripts/lib/site-paths.mjs';
@@ -28,5 +29,5 @@ export const getIconLinks = () => faviconCandidates
28
29
  .filter((candidate) => publicAssetExists(candidate.href))
29
30
  .map((candidate) => ({
30
31
  ...candidate,
31
- href: withBasePath(projectConfig.site.basePath, candidate.href),
32
+ href: withBasePath(projectConfig.site.basePath, publicFilePublication(candidate.href.slice(1)).pathname),
32
33
  }));
@@ -3,6 +3,7 @@ import { getCollection } from 'astro:content';
3
3
  import { assertPageAliasModel, createPageAliasModel } from '../../scripts/lib/page-aliases.mjs';
4
4
  import { getGeneratedSiteRoutes } from '../../scripts/lib/page-aliases.mjs';
5
5
  import { getSitePublicFiles } from '../../scripts/lib/site-link-graph.mjs';
6
+ import { sitePublicDir } from '../../scripts/lib/site-paths.mjs';
6
7
  import projectConfig from '../../scripts/lib/project-config.mjs';
7
8
  import PageAliasRedirect from '../components/PageAliasRedirect.astro';
8
9
  import SearchPage from '../components/SearchPage.astro';
@@ -15,7 +16,7 @@ export async function getStaticPaths() {
15
16
  const aliasModel = assertPageAliasModel(createPageAliasModel({
16
17
  categories: siteModel.categories,
17
18
  pages: siteModel.pages,
18
- publicFiles: await getSitePublicFiles(),
19
+ publicFiles: await getSitePublicFiles(sitePublicDir, projectConfig),
19
20
  generatedRoutes: getGeneratedSiteRoutes({ searchEnabled: projectConfig.search.enabled }),
20
21
  }));
21
22
  const pagesByPathname = new Map(siteModel.pages.map((page) => [page.pathname, page]));
@@ -217,10 +217,19 @@
217
217
  --area-reading-rail-width: 16rem;
218
218
  --area-reading-header-height: 4rem;
219
219
  --site-top-anchor-offset: calc(var(--area-reading-header-height) + 1px + var(--nav-top-safety-inset));
220
- --area-reading-outer-margin: max(var(--content-gutter), calc((100vw - max(var(--page-width), 86rem)) / 2));
220
+ --area-reading-outer-margin: var(--content-gutter);
221
+ --area-reading-frame-width: min(calc(100% - (var(--content-gutter) * 2)), max(var(--page-width), 86rem));
221
222
  --area-reading-content-start: calc(var(--area-reading-outer-margin) + var(--area-reading-rail-width) + var(--local-navigation-gap, clamp(1.5rem, 3vw, 3rem)));
222
223
  }
223
224
 
225
+ /* Keep the reading frame near navigation on wide screens. Focus reading
226
+ hides navigation without moving the article to a different reading axis. */
227
+ :root[data-area-reading-frame] .site-page-layout-tree {
228
+ width: var(--area-reading-frame-width);
229
+ margin-inline: var(--area-reading-outer-margin) auto;
230
+ --tree-layout-outer-margin: max(var(--content-gutter), calc(100vw - var(--content-gutter) - max(var(--page-width), 86rem)));
231
+ }
232
+
224
233
  :root[data-area-reading-frame] .site-page-layout-tree > .site-content {
225
234
  grid-column: 2;
226
235
  }
@@ -242,7 +251,8 @@
242
251
  display: grid;
243
252
  grid-template-columns: var(--area-reading-rail-width) minmax(0, 1fr);
244
253
  column-gap: var(--local-navigation-gap, clamp(1.5rem, 3vw, 3rem));
245
- width: min(calc(100% - (var(--content-gutter) * 2)), max(var(--page-width), 86rem));
254
+ width: var(--area-reading-frame-width);
255
+ margin-inline: var(--area-reading-outer-margin) auto;
246
256
  }
247
257
 
248
258
  :root[data-area-reading-frame]:not([data-focus-reading='on']) .site-nav-leading {
@@ -263,7 +273,6 @@
263
273
 
264
274
  :root[data-area-reading-frame] .site-brand-logo {
265
275
  max-width: 100%;
266
- max-height: 2.25rem;
267
276
  object-fit: contain;
268
277
  }
269
278
 
@@ -333,8 +342,8 @@
333
342
  }
334
343
 
335
344
  :root[data-area-reading-frame] .site-banners {
336
- width: calc(100% - var(--area-reading-content-start) - var(--area-reading-outer-margin));
337
- margin-inline: var(--area-reading-content-start) var(--area-reading-outer-margin);
345
+ width: calc(var(--area-reading-frame-width) - var(--area-reading-rail-width) - var(--local-navigation-gap, clamp(1.5rem, 3vw, 3rem)));
346
+ margin-inline: var(--area-reading-content-start) auto;
338
347
  }
339
348
 
340
349
  :root[data-area-reading-frame]:not([data-focus-reading='on']) .area-navigation-switcher {
@@ -1174,6 +1174,12 @@ ul {
1174
1174
  text-align: left;
1175
1175
  }
1176
1176
 
1177
+ .section-markdown li > :is(ul, ol) {
1178
+ display: block;
1179
+ margin-right: 0;
1180
+ margin-left: 0;
1181
+ }
1182
+
1177
1183
  .section-markdown li + li {
1178
1184
  margin-top: 0.35rem;
1179
1185
  }
@@ -337,6 +337,30 @@
337
337
  object-fit: contain;
338
338
  }
339
339
 
340
+ .managed-markdown-inline {
341
+ display: inline;
342
+ width: auto;
343
+ height: auto;
344
+ max-width: 100%;
345
+ vertical-align: middle;
346
+ }
347
+
348
+ .section-markdown .managed-markdown-standalone {
349
+ margin-block: var(--space-image-item);
350
+ max-width: 100%;
351
+ }
352
+
353
+ .section-markdown :is(ul, ol):has(.managed-markdown-standalone) {
354
+ display: block;
355
+ }
356
+
357
+ .section-markdown li > .managed-markdown-standalone {
358
+ display: inline-grid;
359
+ width: 100%;
360
+ margin-block: 0;
361
+ vertical-align: top;
362
+ }
363
+
340
364
  :root[data-image-inspector-open='true'] {
341
365
  overflow: hidden;
342
366
  }
@@ -1 +1,2 @@
1
+ sourceFormat: 2
1
2
  url: https://example.com/