@parallelworks/tandem-web 0.140.0 → 0.141.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.
@@ -1281,6 +1281,7 @@ var e = {
1281
1281
  description: "A team has its own issues, workflow and members. Its key starts each issue ID, as in ENG-42."
1282
1282
  },
1283
1283
  docs: {
1284
+ spaceDefault: "Start new docs in this space from it",
1284
1285
  duplicate: "Duplicate",
1285
1286
  copyOf: "Copy of {title}",
1286
1287
  subPages: "Sub-pages",
@@ -2239,6 +2240,22 @@ var e = {
2239
2240
  copyLink: "Copy link to heading",
2240
2241
  delete: "Delete"
2241
2242
  },
2243
+ docButton: {
2244
+ untitled: "Button",
2245
+ edit: "Change the button",
2246
+ label: "Label",
2247
+ labelPlaceholder: "Add this week",
2248
+ does: "When pressed",
2249
+ insert: "Insert blocks",
2250
+ makeDoc: "Make a doc",
2251
+ blocks: "Blocks to insert, in Markdown",
2252
+ blocksPlaceholder: "## Week of '{{date}}'\n\n- [ ] ",
2253
+ placeholders: "'{{date}}', '{{time}}' and '{{author}}' are filled in when it's pressed.",
2254
+ template: "Template",
2255
+ chooseTemplate: "Choose a template",
2256
+ parent: "Under",
2257
+ thisDoc: "This doc"
2258
+ },
2242
2259
  linkCard: {
2243
2260
  address: "Address of the page",
2244
2261
  make: "Make card",
@@ -2373,6 +2390,11 @@ var e = {
2373
2390
  hint: "Rows and columns",
2374
2391
  keywords: "grid columns rows"
2375
2392
  },
2393
+ button: {
2394
+ label: "Button",
2395
+ hint: "A button that inserts blocks or makes a doc from a template",
2396
+ keywords: "button template insert repeat weekly"
2397
+ },
2376
2398
  diagram: {
2377
2399
  label: "Diagram",
2378
2400
  hint: "A flowchart, sequence, state or Gantt diagram drawn from Mermaid text",
@@ -1281,6 +1281,7 @@ var e = {
1281
1281
  description: "Un equipo tiene sus propias incidencias, flujo de trabajo y miembros. Su clave inicia el ID de cada incidencia, como en ENG-42."
1282
1282
  },
1283
1283
  docs: {
1284
+ spaceDefault: "Empezar los documentos nuevos de este espacio con ella",
1284
1285
  duplicate: "Duplicar",
1285
1286
  copyOf: "Copia de {title}",
1286
1287
  subPages: "Subpáginas",
@@ -2239,6 +2240,22 @@ var e = {
2239
2240
  copyLink: "Copiar enlace al encabezado",
2240
2241
  delete: "Eliminar"
2241
2242
  },
2243
+ docButton: {
2244
+ untitled: "Botón",
2245
+ edit: "Cambiar el botón",
2246
+ label: "Etiqueta",
2247
+ labelPlaceholder: "Añadir esta semana",
2248
+ does: "Al pulsarlo",
2249
+ insert: "Insertar bloques",
2250
+ makeDoc: "Crear un documento",
2251
+ blocks: "Bloques que inserta, en Markdown",
2252
+ blocksPlaceholder: "## Semana del '{{date}}'\n\n- [ ] ",
2253
+ placeholders: "'{{date}}', '{{time}}' y '{{author}}' se rellenan al pulsarlo.",
2254
+ template: "Plantilla",
2255
+ chooseTemplate: "Elige una plantilla",
2256
+ parent: "Dentro de",
2257
+ thisDoc: "Este documento"
2258
+ },
2242
2259
  linkCard: {
2243
2260
  address: "Dirección de la página",
2244
2261
  make: "Crear tarjeta",
@@ -2373,6 +2390,11 @@ var e = {
2373
2390
  hint: "Filas y columnas",
2374
2391
  keywords: "cuadrícula columnas filas grid columns rows"
2375
2392
  },
2393
+ button: {
2394
+ label: "Botón",
2395
+ hint: "Un botón que inserta bloques o crea un documento desde una plantilla",
2396
+ keywords: "botón plantilla insertar repetir button"
2397
+ },
2376
2398
  diagram: {
2377
2399
  label: "Diagrama",
2378
2400
  hint: "Un diagrama de flujo, secuencia, estados o Gantt a partir de texto Mermaid",
@@ -1281,6 +1281,7 @@ var e = {
1281
1281
  description: "チームには独自の課題、ワークフロー、メンバーがあります。チームのキーは ENG-42 のように各課題 ID の先頭に付きます。"
1282
1282
  },
1283
1283
  docs: {
1284
+ spaceDefault: "このスペースの新しいドキュメントに使う",
1284
1285
  duplicate: "複製",
1285
1286
  copyOf: "{title} のコピー",
1286
1287
  subPages: "サブページ",
@@ -2239,6 +2240,22 @@ var e = {
2239
2240
  copyLink: "見出しへのリンクをコピー",
2240
2241
  delete: "削除"
2241
2242
  },
2243
+ docButton: {
2244
+ untitled: "ボタン",
2245
+ edit: "ボタンを変更",
2246
+ label: "ラベル",
2247
+ labelPlaceholder: "今週を追加",
2248
+ does: "押したとき",
2249
+ insert: "ブロックを挿入",
2250
+ makeDoc: "ドキュメントを作成",
2251
+ blocks: "挿入するブロック(Markdown)",
2252
+ blocksPlaceholder: "## '{{date}}' の週\n\n- [ ] ",
2253
+ placeholders: "'{{date}}'、'{{time}}'、'{{author}}' は押したときに埋まります。",
2254
+ template: "テンプレート",
2255
+ chooseTemplate: "テンプレートを選択",
2256
+ parent: "親",
2257
+ thisDoc: "このドキュメント"
2258
+ },
2242
2259
  linkCard: {
2243
2260
  address: "ページのアドレス",
2244
2261
  make: "カードを作成",
@@ -2373,6 +2390,11 @@ var e = {
2373
2390
  hint: "行と列",
2374
2391
  keywords: "grid columns rows 表 テーブル 行 列"
2375
2392
  },
2393
+ button: {
2394
+ label: "ボタン",
2395
+ hint: "ブロックを挿入したりテンプレートからドキュメントを作るボタン",
2396
+ keywords: "ボタン テンプレート 挿入 button"
2397
+ },
2376
2398
  diagram: {
2377
2399
  label: "図",
2378
2400
  hint: "Mermaid のテキストから描くフローチャート、シーケンス、状態、ガントの図",
@@ -1281,6 +1281,7 @@ var e = {
1281
1281
  description: "팀마다 고유한 이슈, 워크플로, 멤버가 있습니다. 팀 키는 ENG-42처럼 각 이슈 ID의 앞부분이 됩니다."
1282
1282
  },
1283
1283
  docs: {
1284
+ spaceDefault: "이 스페이스의 새 문서에 사용",
1284
1285
  duplicate: "복제",
1285
1286
  copyOf: "{title} 사본",
1286
1287
  subPages: "하위 페이지",
@@ -2239,6 +2240,22 @@ var e = {
2239
2240
  copyLink: "제목 링크 복사",
2240
2241
  delete: "삭제"
2241
2242
  },
2243
+ docButton: {
2244
+ untitled: "버튼",
2245
+ edit: "버튼 변경",
2246
+ label: "레이블",
2247
+ labelPlaceholder: "이번 주 추가",
2248
+ does: "누르면",
2249
+ insert: "블록 넣기",
2250
+ makeDoc: "문서 만들기",
2251
+ blocks: "넣을 블록(Markdown)",
2252
+ blocksPlaceholder: "## '{{date}}' 주\n\n- [ ] ",
2253
+ placeholders: "'{{date}}', '{{time}}', '{{author}}'는 누를 때 채워집니다.",
2254
+ template: "템플릿",
2255
+ chooseTemplate: "템플릿 선택",
2256
+ parent: "상위",
2257
+ thisDoc: "이 문서"
2258
+ },
2242
2259
  linkCard: {
2243
2260
  address: "페이지 주소",
2244
2261
  make: "카드 만들기",
@@ -2373,6 +2390,11 @@ var e = {
2373
2390
  hint: "행과 열",
2374
2391
  keywords: "표 테이블 그리드 열 행 grid columns rows"
2375
2392
  },
2393
+ button: {
2394
+ label: "버튼",
2395
+ hint: "블록을 넣거나 템플릿으로 문서를 만드는 버튼",
2396
+ keywords: "버튼 템플릿 삽입 button"
2397
+ },
2376
2398
  diagram: {
2377
2399
  label: "다이어그램",
2378
2400
  hint: "Mermaid 텍스트로 그리는 순서도, 시퀀스, 상태, 간트 다이어그램",
@@ -1281,6 +1281,7 @@ var e = {
1281
1281
  description: "团队拥有自己的议题、工作流和成员。团队标识会作为每个议题 ID 的开头,例如 ENG-42。"
1282
1282
  },
1283
1283
  docs: {
1284
+ spaceDefault: "此空间的新文档从它开始",
1284
1285
  duplicate: "复制一份",
1285
1286
  copyOf: "{title} 的副本",
1286
1287
  subPages: "子页面",
@@ -2239,6 +2240,22 @@ var e = {
2239
2240
  copyLink: "复制标题链接",
2240
2241
  delete: "删除"
2241
2242
  },
2243
+ docButton: {
2244
+ untitled: "按钮",
2245
+ edit: "更改按钮",
2246
+ label: "标签",
2247
+ labelPlaceholder: "添加本周",
2248
+ does: "按下时",
2249
+ insert: "插入块",
2250
+ makeDoc: "创建文档",
2251
+ blocks: "要插入的块(Markdown)",
2252
+ blocksPlaceholder: "## '{{date}}' 当周\n\n- [ ] ",
2253
+ placeholders: "按下时会填入 '{{date}}'、'{{time}}' 和 '{{author}}'。",
2254
+ template: "模板",
2255
+ chooseTemplate: "选择模板",
2256
+ parent: "上级",
2257
+ thisDoc: "此文档"
2258
+ },
2242
2259
  linkCard: {
2243
2260
  address: "页面地址",
2244
2261
  make: "创建卡片",
@@ -2373,6 +2390,11 @@ var e = {
2373
2390
  hint: "行和列",
2374
2391
  keywords: "表格 网格 列 行 grid columns rows"
2375
2392
  },
2393
+ button: {
2394
+ label: "按钮",
2395
+ hint: "插入块或从模板创建文档的按钮",
2396
+ keywords: "按钮 模板 插入 button"
2397
+ },
2376
2398
  diagram: {
2377
2399
  label: "图表",
2378
2400
  hint: "用 Mermaid 文本绘制的流程图、时序图、状态图或甘特图",
@@ -0,0 +1,221 @@
1
+ import { api as e, unwrap as t } from "../api/client.js";
2
+ import { docQuery as n, docTemplatesQuery as r, docsQuery as i } from "../api/queries.js";
3
+ import { EditIcon as a } from "./icons.js";
4
+ import { docKey as o, docRef as s, useTemplateValues as c } from "../lib/docs.js";
5
+ import { closingFence as l } from "./toggle.js";
6
+ import { Button as u, Combobox as d, IconButton as f, Input as p, Popover as m, SegmentedControl as h, Select as g, Textarea as _ } from "@parallelworks/ui";
7
+ import { useNavigate as v, useParams as y } from "@tanstack/react-router";
8
+ import { useState as b } from "react";
9
+ import { useTranslations as x } from "use-intl";
10
+ import { jsx as S, jsxs as C } from "react/jsx-runtime";
11
+ import { useQuery as w } from "@tanstack/react-query";
12
+ import { Node as T, NodeViewWrapper as E, ReactNodeViewRenderer as D, mergeAttributes as O } from "@tiptap/react";
13
+ // src/components/DocButton.tsx
14
+ var k = /^:::button[ \t]*([^\n]*)\n/, A = /\s+(template|parent)=([\w-]+)/g, j = T.create({
15
+ name: "docButton",
16
+ group: "block",
17
+ atom: !0,
18
+ draggable: !0,
19
+ addAttributes: () => ({
20
+ label: { default: "" },
21
+ action: { default: "insert" },
22
+ blocks: { default: "" },
23
+ template: { default: "" },
24
+ parent: { default: "" }
25
+ }),
26
+ parseHTML: () => [{ tag: "div[data-doc-button]" }],
27
+ renderHTML: ({ node: e, HTMLAttributes: t }) => [
28
+ "div",
29
+ O(t, { "data-doc-button": "" }),
30
+ String(e.attrs.label)
31
+ ],
32
+ addNodeView: () => D(M, { stopEvent: ({ event: e }) => e.target instanceof Element && !!e.target.closest("[data-button-ui]") }),
33
+ markdownTokenName: "docButton",
34
+ markdownTokenizer: {
35
+ name: "docButton",
36
+ level: "block",
37
+ start: (e) => e.search(/^:::button/m),
38
+ tokenize: (e) => {
39
+ let t = k.exec(e);
40
+ if (!t) return;
41
+ let n = e.slice(t[0].length), r = l(n);
42
+ if (r) return {
43
+ type: "docButton",
44
+ raw: e.slice(0, t[0].length + r.index + r.length),
45
+ text: n.slice(0, r.index).trim(),
46
+ head: t[1] ?? ""
47
+ };
48
+ }
49
+ },
50
+ parseMarkdown: (e, t) => {
51
+ let n = String(e.head ?? ""), r = {};
52
+ for (let e of n.matchAll(A)) r[e[1] ?? ""] = e[2] ?? "";
53
+ return t.createNode("docButton", {
54
+ label: n.replace(A, "").trim(),
55
+ action: r.template ? "doc" : "insert",
56
+ blocks: e.text ?? "",
57
+ template: r.template ?? "",
58
+ parent: r.parent ?? ""
59
+ });
60
+ },
61
+ renderMarkdown: (e) => {
62
+ let t = e.attrs ?? {}, n = String(t.label ?? "").replace(/\n/g, " ");
63
+ if (t.action === "doc") {
64
+ let e = t.parent ? ` parent=${t.parent}` : "";
65
+ return `:::button ${n} template=${t.template ?? ""}${e}\n:::`;
66
+ }
67
+ return `:::button ${n}\n\n${String(t.blocks ?? "").trim()}\n\n:::`;
68
+ }
69
+ });
70
+ function M({ node: r, editor: i, getPos: l, updateAttributes: d }) {
71
+ let p = x("docButton"), h = v(), g = c(), { workspace: _ = "", doc: T = "" } = y({ strict: !1 }), { data: D } = w({
72
+ ...n(_, T),
73
+ enabled: !!_ && !!T
74
+ }), [O, k] = b(!1), A = String(r.attrs.label ?? "") || p("untitled"), j = r.attrs.action, M = j === "doc" ? !!r.attrs.template : !!String(r.attrs.blocks ?? "").trim(), P = async () => {
75
+ if (j === "insert") {
76
+ let e = g(), t = Object.entries(e).reduce((e, [t, n]) => e.replaceAll(`{{${t}}}`, n), String(r.attrs.blocks ?? "")), n = l();
77
+ if (n === void 0) return;
78
+ i.chain().focus().insertContentAt(n + r.nodeSize, t, { contentType: "markdown" }).run();
79
+ } else {
80
+ k(!0);
81
+ try {
82
+ let n = String(r.attrs.parent ?? "") || D?.key, i = t(await e.POST("/api/v1/workspaces/{workspace}/docs", {
83
+ params: { path: { workspace: _ } },
84
+ body: {
85
+ template: String(r.attrs.template),
86
+ variables: g(),
87
+ ...n && { parent: n }
88
+ }
89
+ }));
90
+ h({
91
+ to: "/$workspace/docs/$doc",
92
+ params: {
93
+ workspace: _,
94
+ doc: s(i)
95
+ }
96
+ });
97
+ } finally {
98
+ k(!1);
99
+ }
100
+ }
101
+ };
102
+ return /* @__PURE__ */ S(E, {
103
+ className: "doc-button",
104
+ contentEditable: !1,
105
+ children: /* @__PURE__ */ C("div", {
106
+ "data-button-ui": !0,
107
+ className: "flex items-center gap-1",
108
+ children: [/* @__PURE__ */ S(u, {
109
+ size: "sm",
110
+ onClick: () => void P(),
111
+ disabled: !i.isEditable || !M,
112
+ loading: O,
113
+ children: A
114
+ }), i.isEditable && /* @__PURE__ */ S(m, {
115
+ trigger: /* @__PURE__ */ S(f, {
116
+ variant: "ghost",
117
+ size: "xs",
118
+ icon: /* @__PURE__ */ S(a, {}),
119
+ label: p("edit")
120
+ }),
121
+ align: "start",
122
+ className: "w-96",
123
+ initialFocus: "first-control",
124
+ "aria-label": p("edit"),
125
+ children: /* @__PURE__ */ S(N, {
126
+ workspace: _,
127
+ attrs: r.attrs,
128
+ thisDoc: D?.key ?? (T ? o(T) : ""),
129
+ onChange: d
130
+ })
131
+ })]
132
+ })
133
+ });
134
+ }
135
+ function N({ workspace: e, attrs: t, thisDoc: n, onChange: a }) {
136
+ let o = x("docButton"), { data: s = [] } = w(r(e)), { data: c = [] } = w(i(e)), l = t.action;
137
+ return /* @__PURE__ */ C("div", {
138
+ className: "space-y-3 text-sm",
139
+ children: [
140
+ /* @__PURE__ */ C("label", {
141
+ className: "block text-xs font-medium text-muted-foreground",
142
+ children: [o("label"), /* @__PURE__ */ S(p, {
143
+ value: String(t.label ?? ""),
144
+ onChange: (e) => a({ label: e.target.value }),
145
+ placeholder: o("labelPlaceholder"),
146
+ className: "mt-1"
147
+ })]
148
+ }),
149
+ /* @__PURE__ */ S(h, {
150
+ label: o("does"),
151
+ size: "sm",
152
+ value: l,
153
+ onValueChange: (e) => a({ action: e }),
154
+ options: [{
155
+ value: "insert",
156
+ label: o("insert")
157
+ }, {
158
+ value: "doc",
159
+ label: o("makeDoc")
160
+ }]
161
+ }),
162
+ l === "insert" ? /* @__PURE__ */ C("label", {
163
+ className: "block text-xs font-medium text-muted-foreground",
164
+ children: [
165
+ o("blocks"),
166
+ /* @__PURE__ */ S(_, {
167
+ value: String(t.blocks ?? ""),
168
+ onChange: (e) => a({ blocks: e.target.value }),
169
+ rows: 6,
170
+ placeholder: o("blocksPlaceholder"),
171
+ className: "mt-1 font-mono text-xs"
172
+ }),
173
+ /* @__PURE__ */ S("span", {
174
+ className: "mt-1 block font-normal",
175
+ children: o("placeholders")
176
+ })
177
+ ]
178
+ }) : /* @__PURE__ */ C("div", {
179
+ className: "space-y-2",
180
+ children: [/* @__PURE__ */ C("div", {
181
+ className: "text-xs font-medium text-muted-foreground",
182
+ children: [o("template"), /* @__PURE__ */ S("div", {
183
+ className: "mt-1",
184
+ children: /* @__PURE__ */ S(g, {
185
+ value: String(t.template ?? ""),
186
+ items: [{
187
+ value: "",
188
+ label: o("chooseTemplate")
189
+ }, ...s.map((e) => ({
190
+ value: e.id,
191
+ label: e.title
192
+ }))],
193
+ onValueChange: (e) => a({ template: e }),
194
+ "aria-label": o("template"),
195
+ width: "content"
196
+ })
197
+ })]
198
+ }), /* @__PURE__ */ C("div", {
199
+ className: "text-xs font-medium text-muted-foreground",
200
+ children: [o("parent"), /* @__PURE__ */ S("div", {
201
+ className: "mt-1",
202
+ children: /* @__PURE__ */ S(d, {
203
+ items: [{
204
+ value: "",
205
+ label: o("thisDoc")
206
+ }, ...c.filter((e) => e.key !== n).map((e) => ({
207
+ value: e.key,
208
+ label: e.title
209
+ }))],
210
+ value: String(t.parent ?? ""),
211
+ onValueChange: (e) => e !== null && a({ parent: e }),
212
+ "aria-label": o("parent"),
213
+ placeholder: o("parent")
214
+ })
215
+ })]
216
+ })]
217
+ })
218
+ ]
219
+ });
220
+ }
221
+ export { j as DocButton };
@@ -3,87 +3,88 @@ import { Callout as l } from "./callout.js";
3
3
  import { DetailsContent as u, DetailsSummary as d, Toggle as f } from "./toggle.js";
4
4
  import { Columns as p } from "./columns.js";
5
5
  import { DateChip as m } from "./DateChip.js";
6
- import { DocLinkPicker as h } from "./DocLinkPicker.js";
7
- import { IssueListBlock as g } from "./IssueListBlock.js";
8
- import { LinkCard as _ } from "./LinkCard.js";
6
+ import { DocButton as h } from "./DocButton.js";
7
+ import { DocLinkPicker as g } from "./DocLinkPicker.js";
8
+ import { IssueListBlock as _ } from "./IssueListBlock.js";
9
+ import { LinkCard as ee } from "./LinkCard.js";
9
10
  import { MentionPicker as v } from "./MentionPicker.js";
10
11
  import { Diagrams as y, MathBlock as b, MathInline as x } from "./mathDiagrams.js";
11
12
  import { SlashMenu as S } from "./SlashMenu.js";
12
- import { SubPages as ee } from "./SubPages.js";
13
- import { MarkdownTable as te } from "./table.js";
14
- import { useEffect as C, useRef as w } from "react";
15
- import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
16
- import { EditorContent as O, useEditor as k } from "@tiptap/react";
17
- import { CodeBlockLowlight as A } from "@tiptap/extension-code-block-lowlight";
18
- import { TaskItem as j, TaskList as M } from "@tiptap/extension-list";
19
- import { TableKit as N } from "@tiptap/extension-table";
20
- import { Placeholder as P } from "@tiptap/extensions";
21
- import { Markdown as F } from "@tiptap/markdown";
22
- import I from "@tiptap/starter-kit";
23
- import L from "highlight.js/lib/languages/bash";
24
- import R from "highlight.js/lib/languages/css";
25
- import ne from "highlight.js/lib/languages/diff";
26
- import z from "highlight.js/lib/languages/dockerfile";
27
- import B from "highlight.js/lib/languages/go";
28
- import V from "highlight.js/lib/languages/ini";
29
- import H from "highlight.js/lib/languages/javascript";
30
- import U from "highlight.js/lib/languages/json";
31
- import W from "highlight.js/lib/languages/markdown";
32
- import G from "highlight.js/lib/languages/python";
33
- import K from "highlight.js/lib/languages/rust";
34
- import q from "highlight.js/lib/languages/shell";
35
- import J from "highlight.js/lib/languages/sql";
36
- import Y from "highlight.js/lib/languages/typescript";
37
- import X from "highlight.js/lib/languages/xml";
38
- import Z from "highlight.js/lib/languages/yaml";
39
- import { createLowlight as re } from "lowlight";
13
+ import { SubPages as te } from "./SubPages.js";
14
+ import { MarkdownTable as C } from "./table.js";
15
+ import { useEffect as w, useRef as T } from "react";
16
+ import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
17
+ import { EditorContent as k, useEditor as A } from "@tiptap/react";
18
+ import { CodeBlockLowlight as j } from "@tiptap/extension-code-block-lowlight";
19
+ import { TaskItem as M, TaskList as N } from "@tiptap/extension-list";
20
+ import { TableKit as P } from "@tiptap/extension-table";
21
+ import { Placeholder as F } from "@tiptap/extensions";
22
+ import { Markdown as I } from "@tiptap/markdown";
23
+ import L from "@tiptap/starter-kit";
24
+ import R from "highlight.js/lib/languages/bash";
25
+ import ne from "highlight.js/lib/languages/css";
26
+ import z from "highlight.js/lib/languages/diff";
27
+ import B from "highlight.js/lib/languages/dockerfile";
28
+ import V from "highlight.js/lib/languages/go";
29
+ import H from "highlight.js/lib/languages/ini";
30
+ import U from "highlight.js/lib/languages/javascript";
31
+ import W from "highlight.js/lib/languages/json";
32
+ import G from "highlight.js/lib/languages/markdown";
33
+ import K from "highlight.js/lib/languages/python";
34
+ import q from "highlight.js/lib/languages/rust";
35
+ import J from "highlight.js/lib/languages/shell";
36
+ import Y from "highlight.js/lib/languages/sql";
37
+ import X from "highlight.js/lib/languages/typescript";
38
+ import Z from "highlight.js/lib/languages/xml";
39
+ import re from "highlight.js/lib/languages/yaml";
40
+ import { createLowlight as ie } from "lowlight";
40
41
  // src/components/Editor.tsx
41
- var ie = re({
42
- bash: L,
43
- css: R,
44
- diff: ne,
45
- dockerfile: z,
46
- go: B,
47
- ini: V,
48
- javascript: H,
49
- json: U,
50
- markdown: W,
51
- python: G,
52
- rust: K,
53
- shell: q,
54
- sql: J,
55
- typescript: Y,
56
- xml: X,
57
- yaml: Z
42
+ var ae = ie({
43
+ bash: R,
44
+ css: ne,
45
+ diff: z,
46
+ dockerfile: B,
47
+ go: V,
48
+ ini: H,
49
+ javascript: U,
50
+ json: W,
51
+ markdown: G,
52
+ python: K,
53
+ rust: q,
54
+ shell: J,
55
+ sql: Y,
56
+ typescript: X,
57
+ xml: Z,
58
+ yaml: re
58
59
  });
59
- function Q({ editable: h = !0, placeholder: v = "", collaborative: S = !1 } = {}) {
60
+ function Q({ editable: g = !0, placeholder: v = "", collaborative: S = !1 } = {}) {
60
61
  return [
61
- I.configure({
62
+ L.configure({
62
63
  link: {
63
- openOnClick: !h,
64
+ openOnClick: !g,
64
65
  autolink: !0
65
66
  },
66
67
  codeBlock: !1,
67
68
  blockquote: !1,
68
- ...h && !S ? {} : { trailingNode: !1 },
69
+ ...g && !S ? {} : { trailingNode: !1 },
69
70
  ...S ? { undoRedo: !1 } : {}
70
71
  }),
71
- A.configure({ lowlight: ie }),
72
+ j.configure({ lowlight: ae }),
72
73
  l,
73
- M,
74
- j.configure({ nested: !0 }),
74
+ N,
75
+ M.configure({ nested: !0 }),
75
76
  f,
76
- ee,
77
+ te,
77
78
  d,
78
79
  u,
79
80
  p,
80
- N.configure({ table: !1 }),
81
- te.configure({
81
+ P.configure({ table: !1 }),
82
+ C.configure({
82
83
  resizable: !1,
83
84
  allowTableNodeSelection: !1
84
85
  }),
85
- P.configure({ placeholder: v }),
86
- F,
86
+ F.configure({ placeholder: v }),
87
+ I,
87
88
  a,
88
89
  r,
89
90
  n,
@@ -93,11 +94,12 @@ function Q({ editable: h = !0, placeholder: v = "", collaborative: S = !1 } = {}
93
94
  s,
94
95
  c,
95
96
  i,
96
- _,
97
+ h,
98
+ ee,
97
99
  x,
98
100
  b,
99
101
  y,
100
- g,
102
+ _,
101
103
  m
102
104
  ];
103
105
  }
@@ -116,8 +118,8 @@ function $(e) {
116
118
  }
117
119
  };
118
120
  }
119
- function ae({ value: e, onChange: t, onSubmit: n, onBlur: r, onFocus: i, placeholder: a, editable: o = !0, autofocus: s = !1, className: c = "", label: l, onFiles: u, onEditor: d }) {
120
- let f = w({
121
+ function oe({ value: e, onChange: t, onSubmit: n, onBlur: r, onFocus: i, placeholder: a, editable: o = !0, autofocus: s = !1, className: c = "", label: l, onFiles: u, onEditor: d }) {
122
+ let f = T({
121
123
  onChange: t,
122
124
  onSubmit: n,
123
125
  onBlur: r,
@@ -129,9 +131,9 @@ function ae({ value: e, onChange: t, onSubmit: n, onBlur: r, onFocus: i, placeho
129
131
  onBlur: r,
130
132
  onFocus: i
131
133
  };
132
- let p = w(u);
134
+ let p = T(u);
133
135
  p.current = u;
134
- let m = k({
136
+ let m = A({
135
137
  extensions: Q({
136
138
  editable: o,
137
139
  placeholder: a ?? ""
@@ -158,23 +160,23 @@ function ae({ value: e, onChange: t, onSubmit: n, onBlur: r, onFocus: i, placeho
158
160
  onBlur: ({ editor: e }) => f.current.onBlur?.(e.getMarkdown()),
159
161
  onFocus: () => f.current.onFocus?.()
160
162
  });
161
- C(() => {
163
+ w(() => {
162
164
  m && !m.isFocused && m.getMarkdown() !== e && m.commands.setContent(e, {
163
165
  contentType: "markdown",
164
166
  emitUpdate: !1
165
167
  });
166
- }, [m, e]), C(() => {
168
+ }, [m, e]), w(() => {
167
169
  m?.setEditable(o);
168
170
  }, [m, o]);
169
- let g = w(d);
170
- return g.current = d, C(() => (g.current?.(m), () => g.current?.(null)), [m]), /* @__PURE__ */ D(T, { children: [
171
- /* @__PURE__ */ E(O, { editor: m }),
172
- o && /* @__PURE__ */ E(h, { editor: m }),
173
- o && /* @__PURE__ */ E(S, {
171
+ let h = T(d);
172
+ return h.current = d, w(() => (h.current?.(m), () => h.current?.(null)), [m]), /* @__PURE__ */ O(E, { children: [
173
+ /* @__PURE__ */ D(k, { editor: m }),
174
+ o && /* @__PURE__ */ D(g, { editor: m }),
175
+ o && /* @__PURE__ */ D(S, {
174
176
  editor: m,
175
177
  onFiles: p
176
178
  }),
177
- o && /* @__PURE__ */ E(v, { editor: m })
179
+ o && /* @__PURE__ */ D(v, { editor: m })
178
180
  ] });
179
181
  }
180
- export { ae as Editor, Q as contentExtensions, $ as fileHandlers };
182
+ export { oe as Editor, Q as contentExtensions, $ as fileHandlers };
@@ -58,6 +58,7 @@ var w = {
58
58
  file: "↥",
59
59
  table: "▦",
60
60
  docLink: "[[",
61
+ button: "▭",
61
62
  issueList: "☰",
62
63
  date: "@",
63
64
  page: "+",
@@ -103,6 +104,7 @@ var w = {
103
104
  withHeaderRow: !0
104
105
  }),
105
106
  docLink: (e) => e.insertContent("[["),
107
+ button: (e) => e.insertContent({ type: "docButton" }),
106
108
  issueList: (e) => e.insertContent({ type: "issueList" }),
107
109
  date: (e) => e.insertContent([{
108
110
  type: "dateChip",
package/lib/docs.js CHANGED
@@ -1,99 +1,126 @@
1
1
  import { appURL as e, basePath as t } from "./base.js";
2
2
  import { api as n, unwrap as r } from "../api/client.js";
3
3
  import { useNavigate as i } from "./router.js";
4
- import { docsQuery as a, teamsQuery as o } from "../api/queries.js";
5
- import { useFavorites as s } from "./favorites.js";
6
- import { copyItem as c, useLinkItems as l } from "./menus.js";
7
- import { openShare as u } from "./share.js";
8
- import { menuDivider as d } from "@parallelworks/ui";
9
- import { useCallback as f } from "react";
10
- import { useTranslations as p } from "use-intl";
11
- import { useMutation as m, useQuery as h, useQueryClient as g } from "@tanstack/react-query";
4
+ import { docTemplatesQuery as a, docsQuery as o, meQuery as s, teamsQuery as c } from "../api/queries.js";
5
+ import { useFavorites as l } from "./favorites.js";
6
+ import { copyItem as u, useLinkItems as d } from "./menus.js";
7
+ import { openShare as f } from "./share.js";
8
+ import { menuDivider as p } from "@parallelworks/ui";
9
+ import { useCallback as m } from "react";
10
+ import { useFormatter as h, useTranslations as g } from "use-intl";
11
+ import { useMutation as _, useQuery as v, useQueryClient as y } from "@tanstack/react-query";
12
12
  // src/lib/docs.tsx
13
- function _(e) {
13
+ function b(e) {
14
14
  return e.ref || (e.slug ? `${e.slug}-${e.key}` : e.key);
15
15
  }
16
- function v(e) {
16
+ function x(e) {
17
17
  let t = (e.split("-").pop() ?? e).toLowerCase();
18
18
  return /^[a-z2-7]{10}$/.test(t) ? t : e.toLowerCase();
19
19
  }
20
- function y(e, t) {
21
- return `/${e}/docs/${_(t)}`;
20
+ function S() {
21
+ let e = h(), { data: t } = v(s);
22
+ return m(() => {
23
+ let n = /* @__PURE__ */ new Date();
24
+ return {
25
+ date: e.dateTime(n, { dateStyle: "long" }),
26
+ time: e.dateTime(n, { timeStyle: "short" }),
27
+ author: t?.user.name ?? ""
28
+ };
29
+ }, [e, t]);
22
30
  }
23
- function b(e) {
24
- let t = g(), a = i(), o = p("docs"), s = () => t.invalidateQueries({ queryKey: [
31
+ function C(e, t) {
32
+ return `/${e}/docs/${b(t)}`;
33
+ }
34
+ function w(e) {
35
+ let t = y(), o = i(), s = g("docs"), l = () => t.invalidateQueries({ queryKey: [
25
36
  "workspace",
26
37
  e,
27
38
  "docs"
28
- ] }), c = (t) => ({ path: {
39
+ ] }), u = (t) => ({ path: {
29
40
  workspace: e,
30
41
  doc: t
31
- } });
32
- return {
33
- create: m({
34
- mutationFn: async ({ team: t, parent: i, template: a }) => r(await n.POST("/api/v1/workspaces/{workspace}/docs", {
42
+ } }), d = S(), f = _({
43
+ mutationFn: async ({ team: t, parent: i, template: a }) => {
44
+ let o = a ?? await C(t, i);
45
+ return r(await n.POST("/api/v1/workspaces/{workspace}/docs", {
35
46
  params: { path: { workspace: e } },
36
47
  body: {
37
- ...a ? { template: a } : { title: o("untitled") },
48
+ ...o ? {
49
+ template: o,
50
+ variables: d()
51
+ } : { title: s("untitled") },
38
52
  ...t && { team: t },
39
53
  ...i && { parent: i.key }
40
54
  }
41
- })),
42
- onSuccess: (t) => {
43
- s(), a({
44
- to: "/$workspace/docs/$doc",
45
- params: {
46
- workspace: e,
47
- doc: _(t)
48
- },
49
- search: { rename: !0 }
50
- });
55
+ }));
56
+ },
57
+ onSuccess: (t) => {
58
+ l(), o({
59
+ to: "/$workspace/docs/$doc",
60
+ params: {
61
+ workspace: e,
62
+ doc: b(t)
63
+ },
64
+ search: { rename: !0 }
65
+ });
66
+ }
67
+ }), p = _({
68
+ mutationFn: async ({ doc: e, title: t }) => r(await n.PATCH("/api/v1/workspaces/{workspace}/docs/{doc}", {
69
+ params: u(e.key),
70
+ body: { title: t }
71
+ })),
72
+ onSettled: l
73
+ }), m = _({
74
+ mutationFn: async ({ doc: e, area: t }) => r(await n.PATCH("/api/v1/workspaces/{workspace}/docs/{doc}", {
75
+ params: u(e.key),
76
+ body: { area: t }
77
+ })),
78
+ onSettled: l
79
+ }), h = _({
80
+ mutationFn: async ({ doc: e, space: t, parent: i, after: a, before: o }) => r(await n.POST("/api/v1/workspaces/{workspace}/docs/{doc}/move", {
81
+ params: u(e.key),
82
+ body: {
83
+ ...t && { space: t },
84
+ ...i && { parent: i.key },
85
+ ...a && { after: a.key },
86
+ ...o && { before: o.key }
51
87
  }
52
- }),
53
- rename: m({
54
- mutationFn: async ({ doc: e, title: t }) => r(await n.PATCH("/api/v1/workspaces/{workspace}/docs/{doc}", {
55
- params: c(e.key),
56
- body: { title: t }
57
- })),
58
- onSettled: s
59
- }),
60
- fileUnder: m({
61
- mutationFn: async ({ doc: e, area: t }) => r(await n.PATCH("/api/v1/workspaces/{workspace}/docs/{doc}", {
62
- params: c(e.key),
63
- body: { area: t }
64
- })),
65
- onSettled: s
66
- }),
67
- move: m({
68
- mutationFn: async ({ doc: e, space: t, parent: i, after: a, before: o }) => r(await n.POST("/api/v1/workspaces/{workspace}/docs/{doc}/move", {
69
- params: c(e.key),
70
- body: {
71
- ...t && { space: t },
72
- ...i && { parent: i.key },
73
- ...a && { after: a.key },
74
- ...o && { before: o.key }
75
- }
76
- })),
77
- onSettled: s
78
- }),
79
- archive: m({
80
- mutationFn: async (e) => r(await n.POST("/api/v1/workspaces/{workspace}/docs/{doc}/archive", { params: c(e.key) })),
81
- onSettled: s
82
- }),
83
- restore: m({
84
- mutationFn: async (e) => r(await n.POST("/api/v1/workspaces/{workspace}/docs/{doc}/restore", { params: c(e.key) })),
85
- onSettled: s
86
- }),
87
- setTemplate: m({
88
+ })),
89
+ onSettled: l
90
+ }), v = _({
91
+ mutationFn: async (e) => r(await n.POST("/api/v1/workspaces/{workspace}/docs/{doc}/archive", { params: u(e.key) })),
92
+ onSettled: l
93
+ }), x = _({
94
+ mutationFn: async (e) => r(await n.POST("/api/v1/workspaces/{workspace}/docs/{doc}/restore", { params: u(e.key) })),
95
+ onSettled: l
96
+ }), C = async (n, r) => {
97
+ let [i, o] = await Promise.all([t.fetchQuery(a(e)), t.fetchQuery(c(e))]), s = r ? r.teamId ?? "" : o.find((e) => e.key === n)?.id ?? "";
98
+ return i.find((e) => e.spaceDefault && (e.teamId ?? "") === s)?.id;
99
+ }, w = _({
100
+ mutationFn: async ({ doc: e, spaceDefault: t }) => r(await n.PATCH("/api/v1/workspaces/{workspace}/docs/{doc}", {
101
+ params: u(e.key),
102
+ body: { spaceDefault: t }
103
+ })),
104
+ onSettled: l
105
+ });
106
+ return {
107
+ create: f,
108
+ rename: p,
109
+ fileUnder: m,
110
+ move: h,
111
+ archive: v,
112
+ restore: x,
113
+ setTemplate: _({
88
114
  mutationFn: async ({ doc: e, template: t }) => r(await n.PATCH("/api/v1/workspaces/{workspace}/docs/{doc}", {
89
- params: c(e.key),
115
+ params: u(e.key),
90
116
  body: { template: t }
91
117
  })),
92
- onSettled: s
118
+ onSettled: l
93
119
  }),
94
- setLook: m({
120
+ setSpaceDefault: w,
121
+ setLook: _({
95
122
  mutationFn: async ({ doc: e, ...t }) => r(await n.PATCH("/api/v1/workspaces/{workspace}/docs/{doc}", {
96
- params: c(e.key),
123
+ params: u(e.key),
97
124
  body: t
98
125
  })),
99
126
  onMutate: ({ doc: n, ...r }) => {
@@ -106,30 +133,30 @@ function b(e) {
106
133
  ...r
107
134
  } : e);
108
135
  },
109
- onSettled: s
136
+ onSettled: l
110
137
  }),
111
- duplicate: m({
138
+ duplicate: _({
112
139
  mutationFn: async ({ doc: t, team: i }) => {
113
- let a = r(await n.GET("/api/v1/workspaces/{workspace}/docs/{doc}", { params: c(t.key) })), s = a.ancestors?.at(-1)?.key, l = r(await n.POST("/api/v1/workspaces/{workspace}/docs", {
140
+ let a = r(await n.GET("/api/v1/workspaces/{workspace}/docs/{doc}", { params: u(t.key) })), o = a.ancestors?.at(-1)?.key, c = r(await n.POST("/api/v1/workspaces/{workspace}/docs", {
114
141
  params: { path: { workspace: e } },
115
142
  body: {
116
- title: o("copyOf", { title: a.title }),
143
+ title: s("copyOf", { title: a.title }),
117
144
  body: a.body ?? "",
118
145
  ...i && { team: i },
119
- ...s && { parent: s }
146
+ ...o && { parent: o }
120
147
  }
121
148
  }));
122
149
  return a.icon ? r(await n.PATCH("/api/v1/workspaces/{workspace}/docs/{doc}", {
123
- params: c(l.key),
150
+ params: u(c.key),
124
151
  body: { icon: a.icon }
125
- })) : l;
152
+ })) : c;
126
153
  },
127
154
  onSuccess: (t) => {
128
- s(), a({
155
+ l(), o({
129
156
  to: "/$workspace/docs/$doc",
130
157
  params: {
131
158
  workspace: e,
132
- doc: _(t)
159
+ doc: b(t)
133
160
  },
134
161
  search: { rename: !0 }
135
162
  });
@@ -137,73 +164,73 @@ function b(e) {
137
164
  })
138
165
  };
139
166
  }
140
- function x(e) {
141
- let t = i(), n = p("docs"), r = p("common"), m = p("favorites"), g = l(), v = s(e), x = b(e), { data: S = [] } = h(o(e)), { data: w = [] } = h(a(e, { top: !0 }));
142
- return f((i, { onRename: a } = {}) => {
143
- let o = S.find((e) => e.id === i.teamId), s = o?.key ?? "workspace", l = {
167
+ function T(e) {
168
+ let t = i(), n = g("docs"), r = g("common"), a = g("favorites"), s = d(), h = l(e), _ = w(e), { data: y = [] } = v(c(e)), { data: x = [] } = v(o(e, { top: !0 }));
169
+ return m((i, { onRename: o } = {}) => {
170
+ let c = y.find((e) => e.id === i.teamId), l = c?.key ?? "workspace", d = {
144
171
  label: r("open"),
145
172
  onClick: () => t({
146
173
  to: "/$workspace/docs/$doc",
147
174
  params: {
148
175
  workspace: e,
149
- doc: _(i)
176
+ doc: b(i)
150
177
  }
151
178
  })
152
- }, f = [...g(y(e, i)), c(n("copyKey"), i.key)];
179
+ }, m = [...s(C(e, i)), u(n("copyKey"), i.key)];
153
180
  if (i.archivedAt) return [
154
- l,
181
+ d,
155
182
  {
156
183
  label: n("restore"),
157
- onClick: () => x.restore.mutate(i)
184
+ onClick: () => _.restore.mutate(i)
158
185
  },
159
- d,
160
- ...f
186
+ p,
187
+ ...m
161
188
  ];
162
- let p = w.filter((e) => e.id !== i.id), h = [{
189
+ let g = x.filter((e) => e.id !== i.id), v = [{
163
190
  key: "workspace",
164
191
  name: n("workspaceSpace")
165
- }, ...S.filter((e) => !e.archivedAt).map((e) => ({
192
+ }, ...y.filter((e) => !e.archivedAt).map((e) => ({
166
193
  key: e.key,
167
194
  name: e.name
168
195
  }))];
169
196
  return [
170
- l,
197
+ d,
171
198
  {
172
199
  label: n("newInside"),
173
- onClick: () => x.create.mutate({ parent: i })
200
+ onClick: () => _.create.mutate({ parent: i })
174
201
  },
175
202
  {
176
203
  label: n("duplicate"),
177
- onClick: () => x.duplicate.mutate({
204
+ onClick: () => _.duplicate.mutate({
178
205
  doc: i,
179
- team: o?.key
206
+ team: c?.key
180
207
  })
181
208
  },
182
- ...a ? [{
209
+ ...o ? [{
183
210
  label: n("rename"),
184
- onClick: a
211
+ onClick: o
185
212
  }] : [],
186
213
  {
187
214
  label: n("moveTo"),
188
215
  items: [
189
- ...h.map((e) => {
190
- let t = !i.parentId && e.key === s;
216
+ ...v.map((e) => {
217
+ let t = !i.parentId && e.key === l;
191
218
  return {
192
219
  label: n("topOf", { space: e.name }),
193
220
  checked: t,
194
221
  disabled: t,
195
- onClick: () => x.move.mutate({
222
+ onClick: () => _.move.mutate({
196
223
  doc: i,
197
224
  space: e.key
198
225
  })
199
226
  };
200
227
  }),
201
- ...p.length > 0 ? [d] : [],
202
- ...p.map((e) => ({
228
+ ...g.length > 0 ? [p] : [],
229
+ ...g.map((e) => ({
203
230
  label: e.title,
204
231
  checked: e.id === i.parentId,
205
232
  disabled: e.id === i.parentId,
206
- onClick: () => x.move.mutate({
233
+ onClick: () => _.move.mutate({
207
234
  doc: i,
208
235
  parent: e
209
236
  })
@@ -212,64 +239,72 @@ function x(e) {
212
239
  },
213
240
  {
214
241
  label: n("share"),
215
- onClick: () => u({
242
+ onClick: () => f({
216
243
  doc: i.key,
217
244
  title: i.title
218
245
  })
219
246
  },
220
247
  {
221
- label: v.find("doc", i.id) ? m("remove") : m("add"),
222
- onClick: () => v.toggle("doc", i.id)
248
+ label: h.find("doc", i.id) ? a("remove") : a("add"),
249
+ onClick: () => h.toggle("doc", i.id)
223
250
  },
224
251
  {
225
252
  label: n("look.fullWidth"),
226
253
  checked: !!i.fullWidth,
227
- onClick: () => x.setLook.mutate({
254
+ onClick: () => _.setLook.mutate({
228
255
  doc: i,
229
256
  fullWidth: !i.fullWidth
230
257
  })
231
258
  },
232
259
  {
233
260
  label: i.template ? n("stopTemplate") : n("useAsTemplate"),
234
- onClick: () => x.setTemplate.mutate({
261
+ onClick: () => _.setTemplate.mutate({
235
262
  doc: i,
236
263
  template: !i.template
237
264
  })
238
265
  },
266
+ ...i.template ? [{
267
+ label: n("spaceDefault"),
268
+ checked: !!i.spaceDefault,
269
+ onClick: () => _.setSpaceDefault.mutate({
270
+ doc: i,
271
+ spaceDefault: !i.spaceDefault
272
+ })
273
+ }] : [],
239
274
  {
240
275
  label: n("exportMarkdown"),
241
- onClick: () => C(e, { doc: i.key })
276
+ onClick: () => D(e, { doc: i.key })
242
277
  },
243
- d,
244
- ...f,
245
- d,
278
+ p,
279
+ ...m,
280
+ p,
246
281
  {
247
282
  label: n("archive"),
248
283
  destructive: !0,
249
- onClick: () => x.archive.mutate(i)
284
+ onClick: () => _.archive.mutate(i)
250
285
  }
251
286
  ];
252
287
  }, [
253
- S,
254
- w,
288
+ y,
289
+ x,
255
290
  e,
256
291
  t,
257
- g,
258
- v,
259
- x,
292
+ s,
293
+ h,
294
+ _,
260
295
  n,
261
296
  r,
262
- m
297
+ a
263
298
  ]);
264
299
  }
265
- function S(t, n) {
266
- return e(y(t, n));
300
+ function E(t, n) {
301
+ return e(C(t, n));
267
302
  }
268
- function C(e, n) {
303
+ function D(e, n) {
269
304
  let r = new URLSearchParams({
270
305
  ...n,
271
306
  ...n.space && { knowledge: "true" }
272
307
  }), i = document.createElement("a");
273
308
  i.href = `${t}/api/v1/workspaces/${encodeURIComponent(e)}/docs/export?${r}`, i.download = "", i.click();
274
309
  }
275
- export { v as docKey, y as docPath, _ as docRef, S as docURL, C as downloadMarkdown, b as useDocActions, x as useDocMenu };
310
+ export { x as docKey, C as docPath, b as docRef, E as docURL, D as downloadMarkdown, w as useDocActions, T as useDocMenu, S as useTemplateValues };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parallelworks/tandem-web",
3
- "version": "0.140.0",
3
+ "version": "0.141.0",
4
4
  "description": "Tandem's pages, for a host app to render in its own",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
package/styles.css CHANGED
@@ -482,6 +482,10 @@ body[data-tandem] > :not(:has(.tandem)) {
482
482
  display: none;
483
483
  }
484
484
  }
485
+ /* A button that inserts blocks or makes a doc (DocButton). */
486
+ .prose-issue .doc-button {
487
+ margin-block: 0.5em;
488
+ }
485
489
  /* A live list of issues in a doc (IssueListBlock), out of the prose's own styles. */
486
490
  .prose-issue .issue-list-block {
487
491
  margin-block: 0.75em;