Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions internal/richtext/richtext.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,15 @@ var (
// richTextTags are the tags MarkdownToHTML keeps when they are written inside
// Markdown: the content elements of Basecamp's rich text. Any other tag is
// escaped, so prose such as "a Vec<String>" stays text, and so does markup the
// rich text has no element for.
// rich text has no element for. details and summary fold a block of detail
// under a one-line summary, which Basecamp's rich text keeps and shows folded.
var richTextTags = map[string]bool{
"a": true, "b": true, "bc-attachment": true, "blockquote": true, "br": true,
"caption": true, "code": true, "del": true, "div": true, "em": true,
"caption": true, "code": true, "del": true, "details": true, "div": true, "em": true,
"figcaption": true, "figure": true, "h1": true, "h2": true, "h3": true,
"h4": true, "h5": true, "h6": true, "hr": true, "i": true, "img": true,
"li": true, "ol": true, "p": true, "pre": true, "s": true, "span": true,
"strike": true, "strong": true, "table": true, "tbody": true, "td": true,
"strike": true, "strong": true, "summary": true, "table": true, "tbody": true, "td": true,
"tfoot": true, "th": true, "thead": true, "tr": true, "u": true, "ul": true,
}

Expand Down
18 changes: 15 additions & 3 deletions internal/richtext/richtext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,9 +574,21 @@ func TestMarkdownToHTMLConvertsMarkdownBesideHTML(t *testing.T) {
},
{
name: "unsupported inline tag is text",
input: "Use <details>open</details> and <strong>this</strong>",
want: []string{"&lt;details&gt;open&lt;/details&gt;", "<strong>this</strong>"},
notWant: []string{"<details>"},
input: "Use <abbr>open</abbr> and <strong>this</strong>",
want: []string{"&lt;abbr&gt;open&lt;/abbr&gt;", "<strong>this</strong>"},
notWant: []string{"<abbr>"},
},
{
name: "a fold keeps its tags and converts the Markdown inside it",
input: "The fix landed.\n\n<details><summary>How it was measured</summary>\n\n- Five **runs**\n- On [the board](https://example.com/b)\n\n</details>",
want: []string{"<p>The fix landed.</p>", "<details><summary>How it was measured</summary>", "<li>Five <strong>runs</strong></li>", `<a href="https://example.com/b">the board</a>`, "</details>"},
notWant: []string{"&lt;details&gt;", "&lt;summary&gt;", "**runs**"},
},
{
name: "a fold written inline keeps its tags",
input: "Done. <details><summary>Steps</summary>ran it twice</details>",
want: []string{"<details><summary>Steps</summary>ran it twice</details>"},
notWant: []string{"&lt;details&gt;"},
},
{
name: "angle brackets in prose stay text",
Expand Down
4 changes: 4 additions & 0 deletions skills/basecamp-connect/subagent.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ basecamp pings post <bucket_id> - --profile '<profile>' < reply.md
**Rich text.** In comments, messages, documents and cards, the CLI converts
Markdown: headings, **bold**, lists, quotes, fenced code for commands, diffs
and errors, and pipe tables for real grids. Don't hand-write HTML there.
When a reply combines its answer with the detail of the work behind it, fold
that detail: put `<details><summary>What it holds</summary>` and `</details>`
each alone on a line, with blank lines around them and the Markdown between.
Never fold the answer, an ask or a blocker.
Basecamp shows blocks flush against each other unless a blank line in the
Markdown separates them, so leave one between every two blocks, after a
heading and around a list too. In prose, a single newline is no break: it
Expand Down
16 changes: 8 additions & 8 deletions skills/basecamp/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,17 +337,17 @@ A pipe is never consumed implicitly. Only one input may read stdin in an
invocation. Do not use shell-specific ANSI-C quoting such as `$'line 1\nline 2'`
in portable commands.

Basecamp shows blocks flush together unless a blank line in the Markdown
separates them. Leave one between every two blocks, headings and lists
included; in prose, a single newline joins lines into one paragraph. Don't
hand-write HTML: `--format html` is stored as written, so touching blocks and
`<br>`-stacked lines read as a wall of text.
Basecamp shows blocks flush unless a blank line in the Markdown separates
them: leave one between every two blocks, headings and lists too. In prose,
one newline is no break. Don't hand-write HTML: `--format html` is stored as
written, blocks touching. A fold stays Markdown, no `--format html`:
`<details><summary>What</summary>` and `</details>` each alone on a line,
blank lines around, Markdown between.

Rich-text fields support Mermaid diagrams. Put the source in a fenced code block
with the language `mermaid`, and Basecamp shows the diagram to people reading
the post. Previews, emails, and exports show the source, and so does a post
whose source does not parse. Use one where a picture beats prose. Diagram source
spans several lines, so pass it on stdin:
the post; previews, emails, exports and source that doesn't parse show the
text. Use one where a picture beats prose; pass it on stdin:

````bash
basecamp messages create "Release flow" - --in "<project>" --json <<'EOF'
Expand Down
Loading