Detailed changes
@@ -8,7 +8,10 @@ on:
jobs:
build:
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v6
@@ -22,3 +25,6 @@ jobs:
- name: Test
run: go test -v ./...
+
+ - name: Build
+ run: go build -v ./...
@@ -20,6 +20,7 @@ builds:
goos:
- linux
- darwin # macOS
+ - windows
goarch:
- amd64 # Intel
- arm64 # Apple Silicon
@@ -39,10 +40,14 @@ archives:
- # The archive configuration.
# This will create .tar.gz files.
formats: [tar.gz]
+
# A template for the archive file names.
# e.g., matcha_1.2.3_darwin_amd64.tar.gz
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
# Files to include in the archive.
+ format_overrides:
+ - goos: windows
+ formats: ["zip"]
files:
- LICENSE
@@ -36,7 +36,9 @@ You can download pre-compiled binaries from the [Releases page](https://github.c
3. Move the binary to your path:
```bash
mv matcha /usr/local/bin/
+ chmod +x /usr/local/bin/executable_name
```
+
4. Run it:
```bash
matcha
@@ -100,7 +102,22 @@ You can download pre-compiled binaries from the [Releases page](https://github.c
## 🪟 Windows
-Currently, there is no native support for Windows. Please see issue [#123](https://github.com/floatpane/matcha/issues/123) for more details.
+### Manual Binary Download
+
+You can download pre-compiled binaries from the [Releases page](https://github.com/floatpane/matcha/releases).
+
+1. Move the executable to a permanent, dedicated location on your computer (e.g., C:\CLI Tools\MyTool\).
+2. Open the System Properties window by searching for "environment variables" in the Windows Start menu and selecting "Edit the system environment variables".
+3. Click the "Environment Variables..." button at the bottom of the System Properties window, under the "Advanced" tab.
+4. Locate the "Path" variable in the "User variables for [Your Username]" section (for access only by your user account) or the "System variables" section (for all users).
+5. Double-click on the "Path" variable to edit it.
+6. Add the path to your executable's folder:
+
+ In the "Edit environment variable" window, click "New".
+ Type or paste the full path to the folder where your executable is located (e.g., C:\CLI Tools\MyTool\).
+ Click "OK" on all open windows to save the changes.
+
+> Matcha will be added to WinGet as soons as possible!
### WSL
@@ -2,6 +2,7 @@ package main
import (
"archive/tar"
+ "archive/zip"
"bytes"
"compress/gzip"
"encoding/base64"
@@ -1954,7 +1955,13 @@ func runUpdateCLI() error {
return fmt.Errorf("could not write asset to disk: %w", err)
}
- // If it's a tar.gz, extract and find the `matcha` binary
+ // Determine the expected binary name based on the OS.
+ binaryName := "matcha"
+ if runtime.GOOS == "windows" {
+ binaryName = "matcha.exe"
+ }
+
+ // Extract the binary from the archive.
var binPath string
if strings.HasSuffix(assetName, ".tar.gz") || strings.HasSuffix(assetName, ".tgz") {
f, err := os.Open(assetPath)
@@ -1976,9 +1983,8 @@ func runUpdateCLI() error {
return fmt.Errorf("error reading tar: %w", err)
}
name := filepath.Base(hdr.Name)
- if name == "matcha" || strings.Contains(strings.ToLower(name), "matcha") && (hdr.Typeflag == tar.TypeReg) {
- // write out the file
- binPath = filepath.Join(tmpDir, "matcha")
+ if name == binaryName || strings.Contains(strings.ToLower(name), "matcha") && (hdr.Typeflag == tar.TypeReg) {
+ binPath = filepath.Join(tmpDir, binaryName)
out, err := os.Create(binPath)
if err != nil {
return fmt.Errorf("could not create binary file: %w", err)
@@ -1994,6 +2000,38 @@ func runUpdateCLI() error {
break
}
}
+ } else if strings.HasSuffix(assetName, ".zip") {
+ zr, err := zip.OpenReader(assetPath)
+ if err != nil {
+ return fmt.Errorf("could not open zip archive: %w", err)
+ }
+ defer zr.Close()
+ for _, zf := range zr.File {
+ name := filepath.Base(zf.Name)
+ if name == binaryName || strings.Contains(strings.ToLower(name), "matcha") && !zf.FileInfo().IsDir() {
+ rc, err := zf.Open()
+ if err != nil {
+ return fmt.Errorf("could not open file in zip: %w", err)
+ }
+ binPath = filepath.Join(tmpDir, binaryName)
+ out, err := os.Create(binPath)
+ if err != nil {
+ rc.Close()
+ return fmt.Errorf("could not create binary file: %w", err)
+ }
+ if _, err := io.Copy(out, rc); err != nil {
+ out.Close()
+ rc.Close()
+ return fmt.Errorf("could not extract binary: %w", err)
+ }
+ out.Close()
+ rc.Close()
+ if err := os.Chmod(binPath, 0755); err != nil {
+ return fmt.Errorf("could not make binary executable: %w", err)
+ }
+ break
+ }
+ }
} else {
// For non-archive assets, assume the asset is the binary itself.
binPath = assetPath
@@ -2033,7 +2071,16 @@ func runUpdateCLI() error {
in.Close()
out.Close()
- // Attempt to atomically replace
+ // On Windows, a running executable cannot be overwritten directly.
+ // Move the old binary out of the way first, then rename the new one in.
+ if runtime.GOOS == "windows" {
+ oldPath := execPath + ".old"
+ _ = os.Remove(oldPath) // clean up any previous leftover
+ if err := os.Rename(execPath, oldPath); err != nil {
+ return fmt.Errorf("could not move old executable out of the way: %w", err)
+ }
+ }
+
if err := os.Rename(tmpNew, execPath); err != nil {
return fmt.Errorf("could not replace executable: %w", err)
}
@@ -23,7 +23,6 @@ import (
"github.com/floatpane/matcha/theme"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/renderer/html"
- "golang.org/x/sys/unix"
)
func linkStyle() lipgloss.Style {
@@ -57,32 +56,6 @@ func getTerminalCellSize() int {
return defaultCellHeight
}
-// getCellHeightFromFd attempts to get the terminal cell height from a file descriptor.
-// Returns 0 if it fails or if pixel dimensions are not available.
-func getCellHeightFromFd(fd int) int {
- ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
- if err != nil {
- return 0
- }
-
- // ws.Row = number of character rows
- // ws.Ypixel = height in pixels
- // Some terminals don't report pixel dimensions (return 0)
- if ws.Row > 0 && ws.Ypixel > 0 {
- cellHeight := int(ws.Ypixel) / int(ws.Row)
- if cellHeight > 0 {
- debugImageProtocol("terminal cell height: %d pixels (rows=%d, ypixel=%d, fd=%d)", cellHeight, ws.Row, ws.Ypixel, fd)
- return cellHeight
- }
- }
-
- // Terminal reported dimensions but no pixel info - this is common
- if ws.Row > 0 && ws.Ypixel == 0 {
- debugImageProtocol("terminal fd=%d has rows=%d but no pixel info (ypixel=0)", fd, ws.Row)
- }
-
- return 0
-}
// hyperlinkSupported checks if the terminal supports OSC 8 hyperlinks.
func hyperlinkSupported() bool {
@@ -129,7 +102,8 @@ func hyperlinkSupported() bool {
// Check for specific environment variables that indicate hyperlink support
if os.Getenv("KITTY_WINDOW_ID") != "" ||
os.Getenv("GHOSTTY_RESOURCES_DIR") != "" ||
- os.Getenv("WEZTERM_EXECUTABLE") != "" {
+ os.Getenv("WEZTERM_EXECUTABLE") != "" ||
+ os.Getenv("WT_SESSION") != "" {
return true
}
@@ -0,0 +1,32 @@
+//go:build !windows
+
+package view
+
+import "golang.org/x/sys/unix"
+
+// getCellHeightFromFd attempts to get the terminal cell height from a file descriptor.
+// Returns 0 if it fails or if pixel dimensions are not available.
+func getCellHeightFromFd(fd int) int {
+ ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+ if err != nil {
+ return 0
+ }
+
+ // ws.Row = number of character rows
+ // ws.Ypixel = height in pixels
+ // Some terminals don't report pixel dimensions (return 0)
+ if ws.Row > 0 && ws.Ypixel > 0 {
+ cellHeight := int(ws.Ypixel) / int(ws.Row)
+ if cellHeight > 0 {
+ debugImageProtocol("terminal cell height: %d pixels (rows=%d, ypixel=%d, fd=%d)", cellHeight, ws.Row, ws.Ypixel, fd)
+ return cellHeight
+ }
+ }
+
+ // Terminal reported dimensions but no pixel info - this is common
+ if ws.Row > 0 && ws.Ypixel == 0 {
+ debugImageProtocol("terminal fd=%d has rows=%d but no pixel info (ypixel=0)", fd, ws.Row)
+ }
+
+ return 0
+}
@@ -0,0 +1,9 @@
+//go:build windows
+
+package view
+
+// getCellHeightFromFd is not supported on Windows.
+// Returns 0 to fall back to the default cell height.
+func getCellHeightFromFd(fd int) int {
+ return 0
+}