Communitygithub.com

HycJack/nsgo

Verwenden Sie diese Fähigkeit beim Schreiben, Ausführen, Testen, Packen oder Debuggen einer NSGo-App. NSGo ist eine plattformübergreifende native UI-Engine mit Go-Backend, die macOS, iOS, Windows, Linux, Android und das Web unterstützt.

Was ist nsgo?

Dieses Skill unterstützt den gesamten Workflow für die Entwicklung mit **NSGo** (github.com/oneo-me/nsgo), einer plattformübergreifenden nativen UI-Engine, die in Go geschrieben ist und auf macOS, iOS, Windows, Linux, Android und das Web abzielt. Es ist der Go-Port von Native Shell (NSH) und verfolgt einen reaktiven, signalbasierten Ansatz, bei dem App-Logik, das reaktive Graph, Stile, Layout und Text in Go laufen, während jede Plattform mit ihren eigenen nativen Views zeichnet. Der Skill behandelt das Einrichten eines neuen Projekts (go get, go tool nsgo init), das Verständnis der Kern-API (Signale, View, Text, Button, Style, Tailwind-Klassen), fortgeschrittene Animation und Bewegung (core.Motion, Presets, AnimatedStyle, Keyframes, Easing, Feder, Shader-getriebene Bewegung, 3D-Szenen), das Schreiben von Tests mit nsgotest und das Erstellen/Packen von Apps. Eine eiserne Regel ist, niemals Feldnamen zu erraten; da NSGo Vorabversion ist und fast keine öffentliche Dokumentation hat, muss vor dem Schreiben von Code go doc verwendet werden, da ein einziger falscher Feldname zu einem Kompilierungsfehler führt. Das Skill unterstützt auch TSX-Apps auf goja, das Einbetten von NSGo in Host-Apps und gibt eine detaillierte Tabelle mit häufigen Fehlern. Es funktioniert mit Tools wie Claude Code, Cursor und Codex, aber die Übersetzung der Skill.md zielt auf Entwickler ab, die NSGo in beliebigen KI-gestützten Entwicklungsumgebungen verwenden.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/HycJack/nsgo-motion-lab/tree/HEAD

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht nsgo?

Eine Go-UI-Engine: App-Logik, der reaktive Graph, Stile, Layout und Text laufen in Go; jede Plattform zeichnet mit ihren eigenen nativen Views. Apps werden in Go (oder TSX, das zu goja kompiliert wird) geschrieben. Design, Namen und Verhalten sind von NSH (Rust) portiert.

Eiserne Regel: Lies die API, rate sie niemals

NSGo ist eine Vorabversion, hat keine stabile API und fast keine öffentliche Dokumentation im Web, daher sind erinnerte Bezeichner fast immer falsch. Go kann bei einem falschen Feldnamen nicht kompilieren. Bevor Sie eine App schreiben, holen Sie die Wahrheit aus dem Modul selbst:

go doc github.com/oneo-me/nsgo/core      # oder: go doc -all ./core
go doc github.com/oneo-me/nsgo/base

In einem Checkout des Repos sind ARCHITECTURE.md, docs/architecture/*.md (25 Fachdokumente) und examples/ maßgeblich; examples/gallery übt jedes Element, jede Komponente und jeden Dienst. Wenn Sie weder das Modul noch das Repo haben, sagen Sie das – ersetzen Sie nicht ein anderes Framework (Fyne, Gio, Wails) für die tatsächliche Anfrage des Benutzers.

Einrichtung

Erfordert Go 1.26.0+ (go.mod sagt go 1.26.0).

BetriebssystemNatives ZielHinweise
macOS✅Vollständige Toolchain: Erstellt/signiert jedes Ziel mit Go + Xcode
Windows✅Natives + Web-Build und -Ausführung
Linux✅Benötigt GTK 4.14+, Pango, Cairo; WebKitGTK 6.0 und GStreamer für Web-Views/Video

Fügen Sie das Modul und sein Tool einmal zur App hinzu – go tool nsgo führt es dann ohne Installation aus:

go get -tool github.com/oneo-me/nsgo/cmd/nsgo

NSGo veröffentlicht keine getaggten Versionen, daher löst das Modul zu einer Pseudo-Version auf (v0.0.0-2026…-46c7d9c). Einige regionale Mirror geben 404 auf ein ungetaggtes Modul zurück; wenn go get es nicht finden kann, setzen Sie den Proxy fest:

GOPROXY=https://proxy.golang.org,direct GOSUMDB=off go get -tool github.com/oneo-me/nsgo/cmd/nsgo

GOSUMDB=off wird nur benötigt, wenn sum.golang.org nicht erreichbar ist. Arbeiten Sie stattdessen von einem lokalen Checkout aus, verweisen Sie darauf und bereinigen Sie:

go mod edit -replace github.com/oneo-me/nsgo=/path/to/nsgo && go mod tidy

Ein Projekt erstellen

mkdir counter && cd counter
go mod init counter
go get -tool github.com/oneo-me/nsgo/cmd/nsgo   # die Bibliothek und das Werkzeug
go tool nsgo init -name counter .              # schreibt nsgo.yaml, app.go, app_test.go
go mod tidy
go tool nsgo dev .                             # führt es aus; baut bei jedem Speichern neu
go tool nsgo test .                            # headless: 2 bestanden

init benötigt einen Namen. Bloßes go tool nsgo init in einem vorhandenen Verzeichnis schlägt fehl mit "." ist kein App-Name, weil der Standardname vom Verzeichnis stammt – und in . ist das .. Übergeben Sie -name, oder übergeben Sie ein zu erstellendes Verzeichnis:

BefehlEffekt
go tool nsgo init -name Counter .schreibt in das aktuelle Verzeichnis, App-Name Counter
go tool nsgo init Countererstellt ./Counter/ mit der App
go tool nsgo init -template tsx Countererstellt ./Counter/ als TSX-App

init gibt seinen eigenen nächsten Schritt aus (next: go tool nsgo dev Counter) – vertrauen Sie ihm mehr als Ihrem Gedächtnis.

-template ist eines von go (Standard), tsx, mixed. Der App-Name nimmt Buchstaben, Ziffern, Leerzeichen, - und _ an; die Bundle-ID wird dev.nsgo.<name>. Übergeben Sie -module, damit auch go.mod geschrieben wird.

VorlageDateien
gonsgo.yaml, app.go, app_test.go
tsxnsgo.yaml, main.go, App.tsx, app.test.tsx, tsconfig.json, .gitignore
mixeddasselbe, mit App.tsx, das eigenes Go importiert, und einer native.go

nsgo.yaml benennt die App und ihre Ziele:

name: Hallo
id: dev.oneo.nsgo.hallo
version: 0.1.0
dev:
  target: native        # native | web | ios | ios_simulator | android
  hot_reload: true
  fps: false
build:
  output: dist
  targets: [macos_arm64, windows_x64, ios_simulator, android_arm64, web]

Ebenfalls gültig: build_number, appearance, theme, i18n, icon.{apple,png}, dev.icon, android.permissions, sign.{profile,keystore}, web.host. Daneben enthält resources/ ausgelieferte Dateien (geladen als asset:), assets/ enthält nur für das Projekt relevante Dateien (Symbole), locales/ enthält en.yaml, de.yaml… im ICU MessageFormat.

Die App, die Sie erhalten (-template go)

import (
	"fmt"

	"github.com/oneo-me/nsgo"
	"github.com/oneo-me/nsgo/base"
	"github.com/oneo-me/nsgo/core"
)

func Root(cx *core.Scope) core.Node {
	count := core.NewSignal(cx, 0)
	return core.View{Style: core.Style{FlexGrow: 1, Padding: core.All(24), Gap: 16}, Children: []core.Node{
		core.Text{ContentFunc: func() string { return fmt.Sprintf("Gedrückt %d Mal", count.Get()) }},
		base.Button{
			Label:   "Drück mich",
			OnPress: func(core.PressEvent) { count.Update(func(n int) int { return n + 1 }) },
			Style:   core.Style{Background: core.RGB(0x0969da), CornerRadius: 8, Padding: core.Symmetric(10, 16)},
			Children: []core.Node{core.Text{Content: "Drück mich", Style: core.Style{Color: core.RGB(0xffffff)}}},
		},
	}}
}

func main() { nsgo.Run(Root) }

nsgo.Run nimmt entweder eine Root-View (func(cx *core.Scope) core.Node) oder eine App-Funktion (func(app *core.Scope)) entgegen, die eigene Fenster über desktop öffnet. Optionen: WindowSize(w, h), Appearance(...), HideDock(), FPS(bool), WindowMaterial(...), Fonts(paths...), Component(name, factory).

Gedankenmodell

  • Signale. core.NewSignal[T comparable](cx, v) → .Get(), .Set(v), .Update(fn). NewMemo, NewEffect. Der Graph gehört zur Engine-Goroutine und sperrt nie; andere Goroutinen betreten ihn mit Scope.Post.
  • Elemente sind Strukturen; der Nullwert ist der Standard (wie http.Server). Sie setzen nur, was Sie anders haben möchten.
  • Jedes reaktive Feld hat einen Zwilling: Content + ContentFunc func() string, Style + StyleFunc, Class + ClassFunc. XFunc gewinnt über X; das Setzen beider führt beim Mounten zu einem Panic. Felder ohne statische Form nehmen nur die Funktion entgegen (Disabled func() bool, Selected, Expanded).
  • Komponenten sind jeder Typ mit Render(cx *core.Scope) core.Node; ihre Felder sind Eigenschaften. core.Func adaptiert eine einfache Funktion.
  • Regionen – Show, Each, Dynamic, Catch – bauen nur ihren eigenen Bereich neu auf. Each ist eine generische Funktion, keine Methode.
  • Styling: getippte core.Style-Werte oder Tailwind-Klassen in Class ("flex-row gap-4 p-4 bg-white dark:bg-zinc-900"). Die Komponenten von base sind bewusst ungestylt – nur Verhalten, Zustand und Barrierefreiheit.

Bewegung

Zwei Takte. Commit = Go rendert einen Bereich neu (Signale, Show/Each/Dynamic). Frame = der Animator der Plattform bewegt AnimatedStyle-Felder und Shader-Uniforms, ohne Commit. Alle Bewegung gehört zum Frame-Takt; ein während des Renderns gelesener Wert rendert den Teilbaum neu und startet jede Animation darin neu.

BedarfVerwenden Sie
Eine Zustandsänderung (Hover, Drücken, Auswahl, Größenänderung)Style.Transition – eine Dauer, core.EaseOut und eine Properties-Bitmaske; überhaupt kein Graph und unter reduzierter Bewegung übersprungen
Standard-Ein-/Ausgang/Aufmerksamkeit/Schleifecore.Motion + ein core.MotionPreset (38 davon, alle respektieren reduzierte Bewegung)
Benutzerdefinierte Choreografie, Überlappungen, Halten, AustauscheAnimatedStyle + AnimatedNode-Graph (Timing, Spring, Interpolate, Sequence, Stagger, Loop)
Pixelgenau / unendliches Feld / 3Dcore.Shader-Uniforms als Knoten – null Commits
Programmatisches Zeichnencore.Canvas Draw (neu aufzeichnen bei Änderung) oder d.Bind (die Plattform spielt es erneut ab, keine Neuaufzeichnung)
spec := core.FadeInUp.Spec()             // oder Ihre eigene core.MotionSpec
m := core.NewMotion(cx, &spec)           // nil = im Ruhezustand bauen, Play später
return core.View{Style: core.Style{Animated: m.Style()}, Children: []core.Node{card()}}

Fünf Regeln, die mehr wert sind als die API: (1) Lesen Sie niemals ein Signal oder einen animierten Wert während des Renderns – nur Closures oder AnimatedStyle; (2) stylen Sie neu anstatt neu zu bauen; (3) eine endliche Animation behält ihren letzten Wert, daher muss der Frame am Ende der lesbare sein (eine Schleife, die zu ihrem Start zurückkehrt, sieht leer aus, sobald sie sich beruhigt hat); (4) ein animiertes Element benötigt ein gemessenes Elternelement – eine vollständig absolute View misst 0×0 und schneidet alles ab; (5) AnimatedStyle-Felder sind AnimatedNodes, keine Zahlen (core.Constant(1), nicht 1), und es hat kein Width/Height/Perspective.

Lesen Sie references/animation.md, bevor Sie irgendeine Animation schreiben: Tabelle der Voreinstellungen, Easing-/Dauerwerte, die funktioniert haben, Choreografie-Rezepte, Shader-und-Canvas-Bewegung, 3D (Ray Marching), Tests und eine Symptom→Ursache-Tabelle für Bewegungsfehler.

Entwicklungs-Schleife, Build, Test

AufgabeBefehl
Ausführen, bei Speichern neu bauengo tool nsgo dev <verz>
Im Browser ausführengo tool nsgo dev -target web <verz>
Auf Simulator/Gerät ausführengo tool nsgo dev -target ios_simulator|android|ios <verz>
Jeden Commit verfolgen... -trace
Release-Paketego tool nsgo build <verz> (-target all, -output VERZ, -run, -device G)
In eine Host-App einbetten... build -embed → XCFramework (iOS) / AAR (Android) / nsgo.js (Web); Desktops verwenden nsgo.NewView
Testsgo tool nsgo test <verz> (-run REGEXP, -junit DATEI, -target macos|windows|linux|web|ios_simulator|android, -fps)
TSX typprüfengo tool nsgo check <verz>
Nachrichtenkataloge neu generierengo tool nsgo i18n <verz>

Flags überschreiben die nsgo.yaml-Werte, die sie benennen. build.targets und -target akzeptieren all oder Namen wie windows_x64, macos_arm64, linux_x64, ios, web. build schreibt ein Verzeichnis pro Ziel unter -output (Standard dist), benannt <name>-<version>-<target>/. Ein Desktop-Testziel läuft nur auf diesem Betriebssystem (-target windows unter Windows, -target macos auf einem Mac); web, ios_simulator und android laufen von einem Mac.

Tests verhalten sich wie ein Benutzer. nsgotest führt sie headless ohne Fenster aus; -target führt dieselben Tests in einem echten Fenster mit nativen Eingaben und Screenshots aus (app.Screenshot("name")).

func TestMain(m *testing.M) { nsgotest.Main(m) }   // gibt der Plattform den Hauptthread

func TestDrücken(t *testing.T) {
	app := nsgotest.Start(t, Root)
	app.Press("text:Drück mich")
	app.Find("text:1 Mal gedrückt")
}

Selektoren sind die von Playwright: id:, label:, text: (ein Teilstring; text:"Speichern" oder ein bloßer String = gesamter Text), role:<rolle> <name>, view:<n>, /regexp/flags und >> zum Abstieg. Schritte: Press, PressDown/PressUp, LongPress, Key, KeyPress, Type, Focus, Hover, Scroll, ScrollTo, Drag, Touch, Back, Resize, Settle, Advance, WaitFor, WaitForGone, Exists, Query, Texts, Dump. Vollständige Liste: go doc github.com/oneo-me/nsgo/nsgotest.

TSX-Apps haben dieselbe Form: App.tsx importiert mount, signal, Text, View aus "nsgo" (Komponenten aus "nsgo/base") und endet mit mount(App); main.go bettet die Quellen ein und ruft nsgo.Run(compiler.App(src, "App.tsx")) auf. *.test.tsx-Dateien werden als Go-Tests ausgeführt. Siehe references/api.md.

Häufige Fehler

FehlerRealität
Feldnamen erratenNSGo ist eine Vorabversion mit fast keiner öffentlichen Dokumentation, daher sind erinnerte Bezeichner falsch. Zuerst go doc – ein falscher Name ist ein Kompilierungsfehler
Setzen von sowohl Content als auch ContentFuncPanic beim Mounten. Wählen Sie eines aus
NewSignal(cx, []string{...})T muss comparable sein. Verwenden Sie NewSignalFunc mit Ihrem eigenen equal
NewSignal außerhalb eines ScopesSignale benötigen ein cx; der Graph ist Goroutine-lokal, daher ist cx der einzige Handle. Von einer anderen Goroutine aus verwenden Sie Scope.Post
Erwarten, dass Button.Label rendertLabel ist der barrierefreie Name (was role:/label:-Selektoren matchen). Sichtbarer Text kommt von Children
Importieren von internal/...Apps dürfen nur nsgo, core, base, mobile, desktop, i18n, icons, tsx, nsgotest importieren
go build ./... unter Windows oder LinuxSchlägt fehl: internal/platform/apple/edge.go fehlt sein //go:build darwin-Tag und importiert darwin-exklusives purego/objc. Bauen Sie Ihr eigenes Paket (go build .), oder fügen Sie das Tag in einem Checkout hinzu
JustifyContent: core.AlignCenterZwei Enums: AlignItems nimmt core.AlignStart/Center/End/Stretch/Baseline, JustifyContent nimmt core.JustifyStart/Center/End/SpaceBetween/.... Diese zu mischen, ist ein Kompilierungsfehler
core.Shader{OnMouseMove: ...}Ein Shader hat OnPress, OnLayout, Style – keinen Pointer-Move-Handler. Setzen Sie OnMouseMove func(Point) / OnHoverOut func() auf eine core.View darum herum
MustCompileShader allein vertrauenKage kompiliert zur Laufzeit, daher verursacht ein schlechter Shader einen Panic beim Start, nicht beim Build. Testen Sie jede Quelle mit core.CompileShader und melden Sie seinen Fehler
Ein Scroll, das nie scrollt und keine Leiste zeigtScroll hat standardmäßig FlexShrink: 1, aber ein View-Elternteil hat das nicht: Eine Spalte mit nur FlexGrow: 1 wächst auf ihren Inhalt, die Scroll-View erbt diese Höhe und nichts läuft über. Geben Sie der Spalte FlexGrow: 1, FlexShrink: 1 (React Natives flex: 1). Überprüfen Sie es: Der Rahmen des Scrolls muss kürzer als das Fenster sein
Nach einer permanenten Bildlaufleiste suchenDer Indikator ist ein Overlay: Native zeichnet einen abgerundeten Daumen, der während des Scrollens einblendet, 700ms hält und dann in 300ms ausblendet; HideIndicator entfernt ihn. Im Web ist es die eigene Leiste des Browsers (overflow-y: auto, scrollbar-width: auto)
Ein Overlay, das nur mit Left fixiert istEine absolut positionierte View mit einer horizontalen Kante nimmt die verbleibende Breite ihres containing Blocks, nicht ihre Inhaltsbreite: Ein Tooltip bei Left: 34 innerhalb eines 26pt runden Ankers hat nichts mehr übrig und bricht zu einem Zeichen pro Zeile um. Geben Sie ihm eine explizite Width
Alpha innerhalb einer interpolierten FarbeAnimatedStyle.Background, das von core.Interpolate mit Colors: {RGBA(0xffffff, 0.12), ...} gespeist wird, malt als nahezu festes Weiß, nicht mit 12%. Verwenden Sie undurchsichtige Farben für interpolierte Füllungen und behalten Sie Alpha für Style.Background
MotionPreset.Name() als BezeichnungGibt camelCase zurück (fadeIn, zoomIn, breathe), nicht den Namen der Go-Konstante
Eine Animation "springt zum Anfang zurück"Ein Signal oder animierter Wert, der während des Renderns gelesen wird, oder NewAnimatedValue, das innerhalb des Renderings eines Dynamic erstellt wurde: Der Bereich wird neu gebaut und die Werte starten neu. Lesen Sie innerhalb …

Verwandte Skills