diff --git a/CHANGELOG.md b/CHANGELOG.md index 177efa1ab..bc7115a25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Gmail: add `gmail watch pull` for Pub/Sub pull subscription consumers with hook retry support. (#700) — thanks @joshp123. - Docs: add `--tab` and `--all-tabs` to `docs raw` for inspecting specific or complete multi-tab document content. (#697) — thanks @sebsnyk. - Docs: add tab-aware table, image, heading, and paragraph enumerators with structured and plain output. (#719) — thanks @sebsnyk. +- Docs: style locally rendered fenced Markdown blocks with Roboto Mono, dark-green text, and existing paragraph shading. (#676, #724) — thanks @TurboTheTurtle. ### Fixed diff --git a/internal/cmd/docs_find_replace_test.go b/internal/cmd/docs_find_replace_test.go index 40dfdf009..b72bda0fa 100644 --- a/internal/cmd/docs_find_replace_test.go +++ b/internal/cmd/docs_find_replace_test.go @@ -536,11 +536,16 @@ func TestDocsFindReplace_MarkdownCodeBlockStartsFreshParagraphWhenInline(t *test } reqs := batchCalls[0].Requests if len(reqs) != 5 { - t.Fatalf("expected delete, insert, reset, code font, and code shading requests, got %#v", reqs) + t.Fatalf("expected delete, insert, reset, code text style, and code shading requests, got %#v", reqs) } if got := reqs[1].InsertText; got == nil || got.Location.Index != 7 || got.Text != "\nline1"+docsSoftLineBreak+"line2\n" { t.Fatalf("unexpected insert request: %#v", got) } + if got := reqs[3].UpdateTextStyle; got == nil || got.Range.StartIndex != 8 || got.Range.EndIndex != 20 { + t.Fatalf("unexpected code text style request: %#v", got) + } else { + assertFencedCodeTextStyle(t, got) + } if got := reqs[4].UpdateParagraphStyle; got == nil || got.Range.StartIndex != 8 || got.Range.EndIndex != 20 { t.Fatalf("unexpected code shading request: %#v", got) } diff --git a/internal/cmd/docs_formatter.go b/internal/cmd/docs_formatter.go index 59cc4aefc..14691f840 100644 --- a/internal/cmd/docs_formatter.go +++ b/internal/cmd/docs_formatter.go @@ -12,6 +12,13 @@ import ( // same textRun, which lets fenced code blocks keep one shaded paragraph. const docsSoftLineBreak = "\v" +const ( + docsFencedCodeFontFamily = "Roboto Mono" + docsFencedCodeColorRed = 0.09411764705882353 + docsFencedCodeColorGreen = 0.5019607843137255 + docsFencedCodeColorBlue = 0.2196078431372549 +) + // Debug flag for markdown formatter var debugMarkdown = false @@ -92,14 +99,14 @@ func MarkdownToDocsRequests(elements []MarkdownElement, baseIndex int64, tabID s // Embedded line breaks become soft line breaks (vertical tab) so // Docs keeps them inside one paragraph, which lets us apply a // single paragraph-level background shading across the whole block - // instead of emitting one Courier-styled paragraph per source line. + // instead of emitting one styled paragraph per source line. // See #594. codeBody := strings.ReplaceAll(el.Content, "\n", docsSoftLineBreak) codeContent := codeBody + "\n" plainText.WriteString(codeContent) charOffset += utf16Len(codeContent) - // Apply monospace font to entire code block text run. + // Apply fenced code styling to the entire code block text run. requests = append(requests, &docs.Request{ UpdateTextStyle: &docs.UpdateTextStyleRequest{ Range: &docs.Range{ @@ -109,11 +116,20 @@ func MarkdownToDocsRequests(elements []MarkdownElement, baseIndex int64, tabID s }, TextStyle: &docs.TextStyle{ WeightedFontFamily: &docs.WeightedFontFamily{ - FontFamily: "Courier New", + FontFamily: docsFencedCodeFontFamily, Weight: 400, }, + ForegroundColor: &docs.OptionalColor{ + Color: &docs.Color{ + RgbColor: &docs.RgbColor{ + Red: docsFencedCodeColorRed, + Green: docsFencedCodeColorGreen, + Blue: docsFencedCodeColorBlue, + }, + }, + }, }, - Fields: "weightedFontFamily", + Fields: "weightedFontFamily,foregroundColor", }, }) diff --git a/internal/cmd/docs_formatter_test.go b/internal/cmd/docs_formatter_test.go index 1b078eb46..c68fcaf38 100644 --- a/internal/cmd/docs_formatter_test.go +++ b/internal/cmd/docs_formatter_test.go @@ -266,7 +266,7 @@ func TestMarkdownToDocsRequests_MixedListChildrenStayNested(t *testing.T) { // TestMarkdownToDocsRequests_AppendBulletsAndCode is a regression test for // #594. The append path used to inline literal "• " glyphs for bullet lists // (leaving paragraphs as NORMAL_TEXT) and split fenced code blocks into one -// Courier-styled NORMAL_TEXT paragraph per source line with no contiguous +// monospace-styled NORMAL_TEXT paragraph per source line with no contiguous // shading. The fix routes bullets through CreateParagraphBullets and joins // code-block lines with vertical-tab soft breaks so the whole block is one // shaded paragraph. @@ -318,12 +318,18 @@ func TestMarkdownToDocsRequests_AppendBulletsAndCode(t *testing.T) { // - 1 CreateParagraphBullets for the numbered item // - 1 UpdateParagraphStyle with paragraph-level shading covering the // code block - // - 1 UpdateTextStyle for Courier on the code block range + // - 1 UpdateTextStyle for fenced code font/color on the code block range + codeOffset := strings.Index(text, "line 1") + if codeOffset < 0 { + t.Fatalf("test markdown output does not contain code block text: %q", text) + } + codeStart := int64(1 + utf16Len(text[:codeOffset])) + codeEnd := codeStart + int64(utf16Len("line 1"+docsSoftLineBreak+"line 2"+docsSoftLineBreak+"line 3\n")) var ( bulletDisc int bulletNumbered int codeShading int - codeMonospace int + codeTextStyle int bulletPrefixText bool ) for _, r := range requests { @@ -344,8 +350,10 @@ func TestMarkdownToDocsRequests_AppendBulletsAndCode(t *testing.T) { if r.UpdateTextStyle != nil && r.UpdateTextStyle.TextStyle != nil && r.UpdateTextStyle.TextStyle.WeightedFontFamily != nil && - r.UpdateTextStyle.TextStyle.WeightedFontFamily.FontFamily == "Courier New" { - codeMonospace++ + r.UpdateTextStyle.Range.StartIndex == codeStart && + r.UpdateTextStyle.Range.EndIndex == codeEnd { + assertFencedCodeTextStyle(t, r.UpdateTextStyle) + codeTextStyle++ } if r.InsertText != nil && strings.Contains(r.InsertText.Text, "• ") { bulletPrefixText = true @@ -361,10 +369,34 @@ func TestMarkdownToDocsRequests_AppendBulletsAndCode(t *testing.T) { if codeShading != 1 { t.Errorf("expected exactly 1 paragraph shading request for the code block, got %d", codeShading) } - if codeMonospace < 1 { - t.Errorf("expected at least 1 Courier New text style request for the code block, got %d", codeMonospace) + if codeTextStyle != 1 { + t.Errorf("expected exactly 1 fenced code text style request, got %d", codeTextStyle) } if bulletPrefixText { t.Errorf("unexpected literal bullet glyph inside an InsertText request") } } + +func assertFencedCodeTextStyle(t *testing.T, got *docs.UpdateTextStyleRequest) { + t.Helper() + if got == nil { + t.Fatal("missing fenced code text style request") + } + if got.Fields != "weightedFontFamily,foregroundColor" { + t.Fatalf("fenced code fields = %q, want weightedFontFamily,foregroundColor", got.Fields) + } + style := got.TextStyle + if style == nil { + t.Fatal("missing fenced code text style") + } + if style.WeightedFontFamily == nil || style.WeightedFontFamily.FontFamily != docsFencedCodeFontFamily || style.WeightedFontFamily.Weight != 400 { + t.Fatalf("unexpected fenced code font: %#v", style.WeightedFontFamily) + } + if style.ForegroundColor == nil || style.ForegroundColor.Color == nil || style.ForegroundColor.Color.RgbColor == nil { + t.Fatalf("missing fenced code foreground color: %#v", style.ForegroundColor) + } + rgb := style.ForegroundColor.Color.RgbColor + if rgb.Red != docsFencedCodeColorRed || rgb.Green != docsFencedCodeColorGreen || rgb.Blue != docsFencedCodeColorBlue { + t.Fatalf("fenced code foreground = %#v, want #188038", rgb) + } +} diff --git a/internal/cmd/docs_write_markdown_test.go b/internal/cmd/docs_write_markdown_test.go index 0e694a0ab..b8a61baad 100644 --- a/internal/cmd/docs_write_markdown_test.go +++ b/internal/cmd/docs_write_markdown_test.go @@ -795,7 +795,7 @@ func TestDocsWrite_MarkdownAppendStartsStyledBlocksOnFreshParagraph(t *testing.T } reqs := batchRequests[0] if len(reqs) != 4 { - t.Fatalf("expected insert, bullet, code font, and code shading requests, got %#v", reqs) + t.Fatalf("expected insert, bullet, code text style, and code shading requests, got %#v", reqs) } if got := reqs[0].InsertText; got == nil || got.Location.Index != 9 || got.Text != "\nItem\n\nline 1"+docsSoftLineBreak+"line 2\n" { t.Fatalf("unexpected markdown insert: %#v", got) @@ -803,6 +803,11 @@ func TestDocsWrite_MarkdownAppendStartsStyledBlocksOnFreshParagraph(t *testing.T if got := reqs[1].CreateParagraphBullets; got == nil || got.Range.StartIndex != 10 || got.Range.EndIndex != 15 { t.Fatalf("unexpected bullet request: %#v", got) } + if got := reqs[2].UpdateTextStyle; got == nil || got.Range.StartIndex != 16 || got.Range.EndIndex != 30 { + t.Fatalf("unexpected code text style request: %#v", got) + } else { + assertFencedCodeTextStyle(t, got) + } if got := reqs[3].UpdateParagraphStyle; got == nil || got.Range.StartIndex != 16 || got.Range.EndIndex != 30 { t.Fatalf("unexpected code shading request: %#v", got) }