@brett_lamy/docstream-editor 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/package.json +2 -2
- package/src/editor/Editor.tsx +6 -3
- package/src/editor/convert.ts +2 -1
- package/src/editor/extensions.ts +16 -4
- package/src/editor/nodes.tsx +16 -2
- package/src/editor/reference-menu.tsx +88 -21
- package/src/editor/suggestion-menu.tsx +25 -16
- package/src/index.ts +8 -3
- package/src/styles.css +23 -0
package/README.md
CHANGED
|
@@ -109,6 +109,29 @@ The editor supports common ProseMirror/TipTap content plus GitBook-flavored bloc
|
|
|
109
109
|
|
|
110
110
|
Some blocks are intentionally represented as structured nodes rather than fully bespoke editing controls. They are preserved through parse, edit, and serialize flows so the document can continue to round-trip as GitBook-style markdown.
|
|
111
111
|
|
|
112
|
+
## Mentions, channels, and codebases
|
|
113
|
+
|
|
114
|
+
Typing `@`, `#`, or `$` opens a picker and inserts a reference chip that serializes as
|
|
115
|
+
`@id`, `#id`, or `$id` (`$org/repo` works too). Feed each trigger a static list or an
|
|
116
|
+
async `(query) => options` function; options can carry a `label`, `description`, and
|
|
117
|
+
`group` (rendered as section headers). `$` is only enabled when `codebases` is set, so
|
|
118
|
+
dollar signs in ordinary prose never open a menu. Free-form entry is always offered.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<GitbookEditor
|
|
122
|
+
markdown={md}
|
|
123
|
+
onChange={setMd}
|
|
124
|
+
references={{
|
|
125
|
+
mentions: [
|
|
126
|
+
...people.map((p) => ({ id: p.handle, label: p.name, description: p.email, group: "People" })),
|
|
127
|
+
...agents.map((a) => ({ id: a.handle, label: a.name, description: a.harness, group: "Agents" })),
|
|
128
|
+
],
|
|
129
|
+
tags: async (query) => searchChannels(query), // [{ id: "general", description: "channel" }]
|
|
130
|
+
codebases: async (query) => searchRepos(query), // [{ id: "reading-room" }]
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
```
|
|
134
|
+
|
|
112
135
|
## Edit a referenced source file
|
|
113
136
|
|
|
114
137
|
`GitbookEditor` edits the Markdown composition, including a structured
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brett_lamy/docstream-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "TipTap editor for Docstream GitBook-style markdown documents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"./styles.css": "./src/styles.css"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@brett_lamy/docstream": "0.
|
|
36
|
+
"@brett_lamy/docstream": "0.6.0",
|
|
37
37
|
"gpu-lexer": "0.0.2",
|
|
38
38
|
"lucide-react": "^1.17.0"
|
|
39
39
|
},
|
package/src/editor/Editor.tsx
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import { parseMarkdown, serializeMarkdown, type CitationDef } from "@brett_lamy/docstream/gitbook"
|
|
14
14
|
import type { SourceFileSnapshot, SourceReferenceClient } from "@brett_lamy/docstream/source"
|
|
15
15
|
import { astToTiptap, tiptapToAst, type PMNode } from "./convert"
|
|
16
|
-
import { createGitbookExtensions } from "./extensions"
|
|
16
|
+
import { createGitbookExtensions, type ReferenceSources } from "./extensions"
|
|
17
17
|
import { EditorRuntimeProvider } from "./runtime"
|
|
18
18
|
import type { SlashItem } from "./slash-menu"
|
|
19
19
|
|
|
@@ -26,8 +26,11 @@ export interface GitbookEditorProps {
|
|
|
26
26
|
toolbar?: boolean
|
|
27
27
|
/** Built-in "/" slash menu: true (default), false, or a custom item list. */
|
|
28
28
|
slashMenu?: boolean | { items?: SlashItem[] }
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Reference chip pickers: true (default "@"/"#"), false, or per-trigger sources —
|
|
31
|
+
* static lists or async `(query) => options`. A `codebases` source enables "$".
|
|
32
|
+
*/
|
|
33
|
+
references?: boolean | ReferenceSources
|
|
31
34
|
/** Whether the document is editable (default true). */
|
|
32
35
|
editable?: boolean
|
|
33
36
|
/** Placeholder shown in an empty document. */
|
package/src/editor/convert.ts
CHANGED
|
@@ -203,7 +203,8 @@ function pmTextToInline(nodes: PMNode[] | undefined): Inline[] {
|
|
|
203
203
|
.map((n): Inline => {
|
|
204
204
|
if (n.type === "gbReference") {
|
|
205
205
|
const a = n.attrs ?? {}
|
|
206
|
-
const kind =
|
|
206
|
+
const kind =
|
|
207
|
+
a.kind === "tag" || a.kind === "codebase" || a.kind === "citation" ? a.kind : "mention"
|
|
207
208
|
return {
|
|
208
209
|
type: "reference",
|
|
209
210
|
kind,
|
package/src/editor/extensions.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { getSchema, type AnyExtension } from "@tiptap/core"
|
|
|
6
6
|
import type { Schema } from "@tiptap/pm/model"
|
|
7
7
|
import { GbCodeBlock, gitbookNodes } from "./nodes"
|
|
8
8
|
import { SlashMenu, createSlashMenu, type SlashItem } from "./slash-menu"
|
|
9
|
-
import { createReferenceSuggestion } from "./reference-menu"
|
|
9
|
+
import { createReferenceSuggestion, type ReferenceSource } from "./reference-menu"
|
|
10
10
|
|
|
11
11
|
// Carries GitBook's data-view (e.g. "cards") through the editor untouched.
|
|
12
12
|
export const GbTable = Table.extend({
|
|
@@ -15,15 +15,26 @@ export const GbTable = Table.extend({
|
|
|
15
15
|
},
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
+
export interface ReferenceSources {
|
|
19
|
+
/** "@" — people, agents, … */
|
|
20
|
+
mentions?: ReferenceSource
|
|
21
|
+
/** "#" — channels, tags, … */
|
|
22
|
+
tags?: ReferenceSource
|
|
23
|
+
/** "$" — codebases (`$repo` / `$org/repo`). Enables the "$" trigger. */
|
|
24
|
+
codebases?: ReferenceSource
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
export interface GitbookExtensionOptions {
|
|
19
28
|
placeholder?: string
|
|
20
29
|
/** Built-in "/" slash menu: true (default), false, or a custom item list. */
|
|
21
30
|
slashMenu?: boolean | { items?: SlashItem[] }
|
|
22
31
|
/**
|
|
23
|
-
* The
|
|
24
|
-
*
|
|
32
|
+
* The reference chip pickers: true (default: "@" and "#" with free-form entry),
|
|
33
|
+
* false, or per-trigger sources — static lists or async `(query) => options`.
|
|
34
|
+
* "$" (codebases) is only enabled when a `codebases` source is given, so a
|
|
35
|
+
* dollar sign in ordinary prose never opens a menu.
|
|
25
36
|
*/
|
|
26
|
-
references?: boolean |
|
|
37
|
+
references?: boolean | ReferenceSources
|
|
27
38
|
/**
|
|
28
39
|
* Disable StarterKit's built-in undo/redo. Required when wiring Yjs
|
|
29
40
|
* Collaboration, which provides its own shared history.
|
|
@@ -73,6 +84,7 @@ export function createGitbookExtensions(options: GitbookExtensionOptions = {}):
|
|
|
73
84
|
createReferenceSuggestion({ char: "@", ...(opts.mentions ? { items: opts.mentions } : {}) }),
|
|
74
85
|
createReferenceSuggestion({ char: "#", ...(opts.tags ? { items: opts.tags } : {}) })
|
|
75
86
|
)
|
|
87
|
+
if (opts.codebases) list.push(createReferenceSuggestion({ char: "$", items: opts.codebases }))
|
|
76
88
|
}
|
|
77
89
|
|
|
78
90
|
list.push(...extensions)
|
package/src/editor/nodes.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
AtSign,
|
|
14
14
|
CheckCircle2,
|
|
15
15
|
ChevronDown,
|
|
16
|
+
FolderGit2,
|
|
16
17
|
Hash,
|
|
17
18
|
Info,
|
|
18
19
|
Link2,
|
|
@@ -792,7 +793,13 @@ function ReferenceView({ node }: NodeViewProps) {
|
|
|
792
793
|
</span>
|
|
793
794
|
) : (
|
|
794
795
|
<span className={`gb-ref gb-ref-${kind}`}>
|
|
795
|
-
{kind === "mention" ?
|
|
796
|
+
{kind === "mention" ? (
|
|
797
|
+
<AtSign className="gb-ref-icon" />
|
|
798
|
+
) : kind === "codebase" ? (
|
|
799
|
+
<FolderGit2 className="gb-ref-icon" />
|
|
800
|
+
) : (
|
|
801
|
+
<Hash className="gb-ref-icon" />
|
|
802
|
+
)}
|
|
796
803
|
{id}
|
|
797
804
|
</span>
|
|
798
805
|
)}
|
|
@@ -818,7 +825,14 @@ export const GbReference = Node.create({
|
|
|
818
825
|
return [{ tag: "span[data-gb-ref]" }]
|
|
819
826
|
},
|
|
820
827
|
renderHTML({ HTMLAttributes, node }) {
|
|
821
|
-
const sigil =
|
|
828
|
+
const sigil =
|
|
829
|
+
node.attrs.kind === "mention"
|
|
830
|
+
? "@"
|
|
831
|
+
: node.attrs.kind === "tag"
|
|
832
|
+
? "#"
|
|
833
|
+
: node.attrs.kind === "codebase"
|
|
834
|
+
? "$"
|
|
835
|
+
: "^"
|
|
822
836
|
return [
|
|
823
837
|
"span",
|
|
824
838
|
mergeAttributes(HTMLAttributes, { "data-gb-ref": node.attrs.kind, "data-gb-ref-id": node.attrs.id }),
|
|
@@ -1,32 +1,88 @@
|
|
|
1
1
|
import { Extension, type Editor, type Range } from "@tiptap/core"
|
|
2
2
|
import Suggestion from "@tiptap/suggestion"
|
|
3
3
|
import { PluginKey } from "@tiptap/pm/state"
|
|
4
|
-
import { AtSign, Hash } from "lucide-react"
|
|
4
|
+
import { AtSign, FolderGit2, Hash, type LucideIcon } from "lucide-react"
|
|
5
5
|
|
|
6
6
|
import { createSuggestionRender, type SuggestionMenuItem } from "./suggestion-menu"
|
|
7
7
|
|
|
8
|
-
export type ReferenceTrigger = "@" | "#"
|
|
8
|
+
export type ReferenceTrigger = "@" | "#" | "$"
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
/** One pickable reference: the chip stores `id`; the rest only decorates the menu. */
|
|
11
|
+
export interface ReferenceOption {
|
|
11
12
|
id: string
|
|
13
|
+
/** Display name shown next to the id (e.g. "Luna" for `@luna`). */
|
|
14
|
+
label?: string
|
|
15
|
+
/** Secondary line (e.g. an email, "agent · claude-code", a topic). */
|
|
16
|
+
description?: string
|
|
17
|
+
/** Section header; consecutive options with the same group are listed together. */
|
|
18
|
+
group?: string
|
|
12
19
|
}
|
|
13
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Where a trigger's options come from: a static list (filtered by the editor), or a
|
|
23
|
+
* function the host answers per query — sync or async, already filtered.
|
|
24
|
+
*/
|
|
25
|
+
export type ReferenceSource =
|
|
26
|
+
| readonly (string | ReferenceOption)[]
|
|
27
|
+
| ((query: string) => readonly ReferenceOption[] | Promise<readonly ReferenceOption[]>)
|
|
28
|
+
|
|
14
29
|
export interface ReferenceSuggestionOptions {
|
|
15
30
|
char: ReferenceTrigger
|
|
16
|
-
/**
|
|
17
|
-
items?:
|
|
31
|
+
/** Options offered in the picker; free-form entry works unless disabled. */
|
|
32
|
+
items?: ReferenceSource
|
|
33
|
+
/** Offer inserting the raw query when nothing matches exactly (default true). */
|
|
34
|
+
allowFreeForm?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ReferenceItem extends SuggestionMenuItem {
|
|
38
|
+
id: string
|
|
18
39
|
}
|
|
19
40
|
|
|
20
|
-
const KIND: Record<ReferenceTrigger, "mention" | "tag"
|
|
41
|
+
const KIND: Record<ReferenceTrigger, "mention" | "tag" | "codebase"> = {
|
|
42
|
+
"@": "mention",
|
|
43
|
+
"#": "tag",
|
|
44
|
+
$: "codebase",
|
|
45
|
+
}
|
|
46
|
+
const ICON: Record<ReferenceTrigger, LucideIcon> = { "@": AtSign, "#": Hash, $: FolderGit2 }
|
|
47
|
+
const EMPTY: Record<ReferenceTrigger, string> = {
|
|
48
|
+
"@": "Type a name to mention",
|
|
49
|
+
"#": "Type a channel or tag",
|
|
50
|
+
$: "Type a codebase",
|
|
51
|
+
}
|
|
52
|
+
const MAX_OPTIONS = 50
|
|
53
|
+
|
|
54
|
+
function normalize(option: string | ReferenceOption): ReferenceOption {
|
|
55
|
+
return typeof option === "string" ? { id: option } : option
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function matches(option: ReferenceOption, query: string): boolean {
|
|
59
|
+
if (!query) return true
|
|
60
|
+
return [option.id, option.label ?? ""].some((value) => value.toLowerCase().includes(query))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Resolve a source for a query into options (exported for hosts' tests). */
|
|
64
|
+
export async function resolveReferenceOptions(
|
|
65
|
+
source: ReferenceSource | undefined,
|
|
66
|
+
query: string,
|
|
67
|
+
): Promise<ReferenceOption[]> {
|
|
68
|
+
if (source === undefined) return []
|
|
69
|
+
if (typeof source === "function") return [...(await source(query))].slice(0, MAX_OPTIONS)
|
|
70
|
+
const q = query.toLowerCase()
|
|
71
|
+
return source.map(normalize).filter((option) => matches(option, q)).slice(0, MAX_OPTIONS)
|
|
72
|
+
}
|
|
21
73
|
|
|
22
74
|
/**
|
|
23
|
-
* An `@mention` / `#tag` picker built on @tiptap/suggestion.
|
|
24
|
-
*
|
|
25
|
-
* works with no
|
|
75
|
+
* An `@mention` / `#tag` / `$codebase` picker built on @tiptap/suggestion. Options come
|
|
76
|
+
* from the host (static or async) and the raw query is offered as a free-form entry,
|
|
77
|
+
* so `@anything⏎` works with no source configured.
|
|
26
78
|
*/
|
|
27
|
-
export function createReferenceSuggestion({
|
|
79
|
+
export function createReferenceSuggestion({
|
|
80
|
+
char,
|
|
81
|
+
items,
|
|
82
|
+
allowFreeForm = true,
|
|
83
|
+
}: ReferenceSuggestionOptions) {
|
|
28
84
|
const kind = KIND[char]
|
|
29
|
-
const icon = char
|
|
85
|
+
const icon = ICON[char]
|
|
30
86
|
return Extension.create({
|
|
31
87
|
name: `${kind}Suggestion`,
|
|
32
88
|
addProseMirrorPlugins() {
|
|
@@ -39,13 +95,26 @@ export function createReferenceSuggestion({ char, items = [] }: ReferenceSuggest
|
|
|
39
95
|
// Each Suggestion plugin needs its own key — the default is shared
|
|
40
96
|
// with the slash menu and would collide.
|
|
41
97
|
pluginKey: new PluginKey(`${kind}Suggestion`),
|
|
42
|
-
items: ({ query }) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
98
|
+
items: async ({ query }) => {
|
|
99
|
+
let options: ReferenceOption[]
|
|
100
|
+
try {
|
|
101
|
+
options = await resolveReferenceOptions(items, query)
|
|
102
|
+
} catch {
|
|
103
|
+
options = []
|
|
104
|
+
}
|
|
105
|
+
const menu: ReferenceItem[] = options.map((option) => ({
|
|
106
|
+
id: option.id,
|
|
107
|
+
title: `${char}${option.id}`,
|
|
108
|
+
icon,
|
|
109
|
+
...(option.label || option.description
|
|
110
|
+
? { description: [option.label, option.description].filter(Boolean).join(" · ") }
|
|
111
|
+
: {}),
|
|
112
|
+
...(option.group ? { group: option.group } : {}),
|
|
113
|
+
}))
|
|
114
|
+
const exact = options.some((option) => option.id.toLowerCase() === query.toLowerCase())
|
|
115
|
+
return allowFreeForm && query && !exact
|
|
116
|
+
? [...menu, { id: query, title: `${char}${query}`, icon }]
|
|
117
|
+
: menu
|
|
49
118
|
},
|
|
50
119
|
command: ({ editor, range, props }) => {
|
|
51
120
|
;(editor as Editor)
|
|
@@ -58,9 +127,7 @@ export function createReferenceSuggestion({ char, items = [] }: ReferenceSuggest
|
|
|
58
127
|
])
|
|
59
128
|
.run()
|
|
60
129
|
},
|
|
61
|
-
render: createSuggestionRender<ReferenceItem>(
|
|
62
|
-
char === "@" ? "Type a name to mention" : "Type a tag"
|
|
63
|
-
),
|
|
130
|
+
render: createSuggestionRender<ReferenceItem>(EMPTY[char]),
|
|
64
131
|
}),
|
|
65
132
|
]
|
|
66
133
|
},
|
|
@@ -12,11 +12,15 @@ import type { SuggestionProps } from "@tiptap/suggestion"
|
|
|
12
12
|
import { ReactRenderer } from "@tiptap/react"
|
|
13
13
|
import type { LucideIcon } from "lucide-react"
|
|
14
14
|
|
|
15
|
-
// Shared popup used by the "/" slash menu and the "@"/"#" reference menus.
|
|
15
|
+
// Shared popup used by the "/" slash menu and the "@"/"#"/"$" reference menus.
|
|
16
16
|
|
|
17
17
|
export interface SuggestionMenuItem {
|
|
18
18
|
title: string
|
|
19
19
|
icon: LucideIcon
|
|
20
|
+
/** Secondary text shown after the title (e.g. a display name or email). */
|
|
21
|
+
description?: string
|
|
22
|
+
/** Section header; shown when it differs from the previous item's group. */
|
|
23
|
+
group?: string
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export interface MenuProps<Item extends SuggestionMenuItem> {
|
|
@@ -97,21 +101,26 @@ export const SuggestionMenuView = forwardRef<MenuHandle, MenuProps<SuggestionMen
|
|
|
97
101
|
>
|
|
98
102
|
{items.length === 0 && <div className="slash-empty">{emptyLabel}</div>}
|
|
99
103
|
{items.map((item, i) => (
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
<div key={`${item.group ?? ""}:${item.title}:${i}`} className="slash-row">
|
|
105
|
+
{item.group && item.group !== items[i - 1]?.group && (
|
|
106
|
+
<div className="slash-group">{item.group}</div>
|
|
107
|
+
)}
|
|
108
|
+
<button
|
|
109
|
+
ref={(el) => {
|
|
110
|
+
itemRefs.current[i] = el
|
|
111
|
+
}}
|
|
112
|
+
className={`slash-item ${i === index ? "slash-item-active" : ""}`}
|
|
113
|
+
onMouseEnter={() => setIndex(i)}
|
|
114
|
+
onMouseDown={(e) => {
|
|
115
|
+
e.preventDefault()
|
|
116
|
+
command(item)
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
<item.icon className="size-4" />
|
|
120
|
+
<span>{item.title}</span>
|
|
121
|
+
{item.description && <span className="slash-item-description">{item.description}</span>}
|
|
122
|
+
</button>
|
|
123
|
+
</div>
|
|
115
124
|
))}
|
|
116
125
|
</div>
|
|
117
126
|
)
|
package/src/index.ts
CHANGED
|
@@ -6,12 +6,17 @@ export type { PMNode } from "./editor/convert"
|
|
|
6
6
|
// Building blocks for composing a custom editor (e.g. with Yjs collaboration
|
|
7
7
|
// or a bespoke slash menu) instead of the batteries-included GitbookEditor.
|
|
8
8
|
export { createGitbookExtensions, getGitbookSchema, GbTable } from "./editor/extensions"
|
|
9
|
-
export type { GitbookExtensionOptions } from "./editor/extensions"
|
|
9
|
+
export type { GitbookExtensionOptions, ReferenceSources } from "./editor/extensions"
|
|
10
10
|
export { gitbookNodes, GbCodeBlock, GbReference } from "./editor/nodes"
|
|
11
11
|
export { SlashMenu, createSlashMenu, SLASH_ITEMS } from "./editor/slash-menu"
|
|
12
12
|
export type { SlashItem } from "./editor/slash-menu"
|
|
13
|
-
export { createReferenceSuggestion } from "./editor/reference-menu"
|
|
14
|
-
export type {
|
|
13
|
+
export { createReferenceSuggestion, resolveReferenceOptions } from "./editor/reference-menu"
|
|
14
|
+
export type {
|
|
15
|
+
ReferenceOption,
|
|
16
|
+
ReferenceSource,
|
|
17
|
+
ReferenceSuggestionOptions,
|
|
18
|
+
ReferenceTrigger,
|
|
19
|
+
} from "./editor/reference-menu"
|
|
15
20
|
export { SuggestionMenuView, createSuggestionRender } from "./editor/suggestion-menu"
|
|
16
21
|
export type { SuggestionMenuItem } from "./editor/suggestion-menu"
|
|
17
22
|
export { ReactDemoEditor } from "./editor/ReactDemoEditor"
|
package/src/styles.css
CHANGED
|
@@ -650,6 +650,23 @@
|
|
|
650
650
|
.slash-item-active { background: var(--accent, #f4f4f5); }
|
|
651
651
|
.slash-item svg { color: var(--muted-foreground, #6b7280); }
|
|
652
652
|
.slash-empty { padding: 8px 10px; font-size: 13px; color: var(--muted-foreground, #6b7280); }
|
|
653
|
+
.slash-group {
|
|
654
|
+
padding: 8px 10px 3px;
|
|
655
|
+
font-size: 11px;
|
|
656
|
+
font-weight: 600;
|
|
657
|
+
letter-spacing: 0.04em;
|
|
658
|
+
text-transform: uppercase;
|
|
659
|
+
color: var(--muted-foreground, #6b7280);
|
|
660
|
+
}
|
|
661
|
+
.slash-item-description {
|
|
662
|
+
margin-left: auto;
|
|
663
|
+
padding-left: 12px;
|
|
664
|
+
overflow: hidden;
|
|
665
|
+
font-size: 12px;
|
|
666
|
+
white-space: nowrap;
|
|
667
|
+
text-overflow: ellipsis;
|
|
668
|
+
color: var(--muted-foreground, #6b7280);
|
|
669
|
+
}
|
|
653
670
|
|
|
654
671
|
.gb-code-live {
|
|
655
672
|
display: flex;
|
|
@@ -819,6 +836,12 @@
|
|
|
819
836
|
color: var(--gb-muted-foreground);
|
|
820
837
|
}
|
|
821
838
|
|
|
839
|
+
.gb-ref-codebase {
|
|
840
|
+
border: 1px solid rgba(34, 197, 94, 0.45);
|
|
841
|
+
background: rgba(34, 197, 94, 0.1);
|
|
842
|
+
color: rgb(22, 163, 74);
|
|
843
|
+
}
|
|
844
|
+
|
|
822
845
|
.gb-cite {
|
|
823
846
|
display: inline-block;
|
|
824
847
|
padding: 1px 5px;
|