Skip to content
Closed
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- CLI: preserve Unicode shaping joiners in human-readable names and emoji while retaining strict terminal-control sanitization in diagnostics.
- Client: reject responses larger than 1 MiB without decoding truncated data, and report unhandled HTTP redirects as API errors instead of successful results.
- Homebrew: install through a Formula (`brew install openclaw/tap/goplaces`) instead of the retired Cask; quarantine is preserved.

Expand Down
40 changes: 40 additions & 0 deletions internal/cli/joiners_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cli

import (
"bytes"
"strings"
"testing"

"github.com/steipete/goplaces"
)

func TestRenderSearchPreservesUnicodeShapingJoiners(t *testing.T) {
name := "👩\u200d🔬 می\u200cخواهم"
output := renderSearch(NewColor(false), goplaces.SearchResponse{Results: []goplaces.PlaceSummary{{Name: name, Address: "👩\u200d🔬"}}})
if !strings.Contains(output, name) {
t.Fatalf("name changed: %q", output)
}
if strings.Count(output, "\u200d") != 2 {
t.Fatalf("address joiner lost: %q", output)
}
}

func TestRenderJoinersStillRemovesTerminalAndBidiControls(t *testing.T) {
name := "👩\u200d🔬 safe\u202egnirts\u2066\u200b\ufeff\x1b[31m\x07"
output := renderSearch(NewColor(false), goplaces.SearchResponse{Results: []goplaces.PlaceSummary{{Name: name}}})
if !strings.Contains(output, "👩\u200d🔬") {
t.Fatalf("emoji shaping lost: %q", output)
}
if strings.ContainsAny(output, "\u202e\u2066\u200b\ufeff\x1b\x07") {
t.Fatalf("unsafe display controls: %q", output)
}
}

func TestErrorSanitizerKeepsStrictFormatBoundary(t *testing.T) {
var buffer bytes.Buffer
writeError(&buffer, "safe\u200c\u200d\u202e\u2066\u200b\ufeff\x1b")
output := buffer.String()
if output != "safe\n" {
t.Fatalf("error sanitizer changed: %q", output)
}
}
12 changes: 11 additions & 1 deletion internal/cli/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,17 @@ func directionsStepLine(step goplaces.DirectionsStep) string {
return strings.Join(parts, " · ")
}

// Human output preserves shaping joiners used in names and emoji, while
// diagnostics keep the stricter format-control boundary.
func sanitizeTerminalText(value string) string {
return sanitizeText(value, true)
}

func sanitizeTerminalErrorText(value string) string {
return sanitizeText(value, false)
}

func sanitizeText(value string, preserveJoiners bool) string {
if value == "" {
return ""
}
Expand All @@ -463,7 +473,7 @@ func sanitizeTerminalText(value string) string {
if unicode.IsControl(r) {
continue
}
if unicode.Is(unicode.Cf, r) {
if unicode.Is(unicode.Cf, r) && (!preserveJoiners || (r != '\u200c' && r != '\u200d')) {
continue
}
out.WriteRune(r)
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,5 @@ func handleError(writer io.Writer, err error) int {
}

func writeError(writer io.Writer, message string) {
_, _ = fmt.Fprintln(writer, sanitizeTerminalText(message))
_, _ = fmt.Fprintln(writer, sanitizeTerminalErrorText(message))
}
Loading