From 94b7a25e340d3f8ca69c1cecefdad5a7406fbd83 Mon Sep 17 00:00:00 2001 From: carl chen Date: Mon, 12 Oct 2026 15:35:00 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20macOS=20=E6=89=93=E5=8C=85=E4=B8=BA?= =?UTF-8?q?=20.app=20+=20DMG=EF=BC=8C=E8=A7=A3=E5=86=B3=20Gatekeeper=20?= =?UTF-8?q?=E5=92=8C=E8=A3=B8=E4=BA=8C=E8=BF=9B=E5=88=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 scripts/build-macos-app.sh:构建 .app bundle、ad-hoc 签名、生成 DMG - CI macOS 分支改为上传 DMG 而非裸二进制 - 版本号升至 0.2.0 Co-Authored-By: Claude Code --- .github/workflows/release.yml | 23 +++++- package.json | 4 +- scripts/build-macos-app.sh | 147 ++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 3 deletions(-) create mode 100755 scripts/build-macos-app.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 632546f..7204025 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -104,7 +104,19 @@ jobs: echo "UPX not available, skipping compression" fi - - name: Upload binary + - name: Build macOS DMG + if: matrix.os == 'macos-latest' + shell: bash + env: + RELEASE_TAG: ${{ github.ref_type == 'tag' && github.ref_name || inputs.tag }} + run: | + # 安装 create-dmg(比裸 hdiutil 好看得多) + brew install create-dmg || true + chmod +x scripts/build-macos-app.sh + scripts/build-macos-app.sh "native/${{ matrix.output }}" "resources/icon.icns" "${RELEASE_TAG#v}" + + - name: Upload binary (Linux/Windows) + if: matrix.os != 'macos-latest' uses: actions/upload-artifact@v7 with: name: ${{ matrix.artifact }}-release @@ -112,6 +124,15 @@ jobs: if-no-files-found: error retention-days: 7 + - name: Upload DMG (macOS) + if: matrix.os == 'macos-latest' + uses: actions/upload-artifact@v7 + with: + name: macos-release + path: dist/Muse-*-macOS.dmg + if-no-files-found: error + retention-days: 7 + publish: name: Publish GitHub Release needs: build diff --git a/package.json b/package.json index d9796a6..f600615 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "muse", - "version": "0.1.3", + "version": "0.2.0", "private": true, "license": "MIT", "description": "Muse monorepo 根 — 纯编排(前端在 app/,Go 壳在 native/)", @@ -18,4 +18,4 @@ "devDependencies": { "esbuild": "^0.28.1" } -} \ No newline at end of file +} diff --git a/scripts/build-macos-app.sh b/scripts/build-macos-app.sh new file mode 100755 index 0000000..e681ae9 --- /dev/null +++ b/scripts/build-macos-app.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# build-macos-app.sh — 将 Go 二进制打包为 macOS .app bundle 并生成 DMG +# +# 用法(CI 中调用): +# scripts/build-macos-app.sh +# +# 输出: +# dist/Muse.app — 完整的 .app bundle +# dist/Muse--macOS.dmg — 安装镜像 + +set -euo pipefail + +BINARY="${1:?Usage: $0 }" +ICON="${2:?Usage: $0 }" +VERSION="${3:?Usage: $0 }" + +APP_NAME="Muse" +BUNDLE_ID="com.muse.app" +DIST_DIR="$(cd "$(dirname "$0")/.." && pwd)/dist" +STAGE="$DIST_DIR/stage" +DMG_OUT="$DIST_DIR/Muse-${VERSION}-macOS.dmg" + +rm -rf "$DIST_DIR" +mkdir -p "$STAGE" + +# ── 1. 构建 .app bundle 结构 ────────────────────────────────────────────── + +APP="$STAGE/${APP_NAME}.app" +mkdir -p "$APP/Contents/MacOS" +mkdir -p "$APP/Contents/Resources" + +# 复制二进制 +cp "$BINARY" "$APP/Contents/MacOS/${APP_NAME}" +chmod +x "$APP/Contents/MacOS/${APP_NAME}" + +# 复制图标 +cp "$ICON" "$APP/Contents/Resources/icon.icns" + +# ── 2. Info.plist ───────────────────────────────────────────────────────── + +cat > "$APP/Contents/Info.plist" << PLIST + + + + + CFBundleExecutable + ${APP_NAME} + CFBundleIconFile + icon + CFBundleIdentifier + ${BUNDLE_ID} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${APP_NAME} + CFBundleDisplayName + ${APP_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${VERSION} + CFBundleVersion + ${VERSION} + LSMinimumSystemVersion + 13.0 + NSHighResolutionCapable + + NSPrincipalClass + NSApplication + LSApplicationCategoryType + public.app-category.productivity + + +PLIST + +echo "✅ .app bundle 创建完成: $APP" + +# ── 3. Ad-hoc 签名(让 Gatekeeper 至少知道这是一个签名过的应用)────────────── + +# 没有 Apple Developer ID 时做 ad-hoc 签名 +# 有证书时可以在 CI 里用 Developer ID Application 证书替换这步 +codesign --force --deep --sign - "$APP" 2>/dev/null || true + +# ── 4. 生成 DMG ─────────────────────────────────────────────────────────── + +if command -v create-dmg &>/dev/null; then + echo "使用 create-dmg 生成 DMG..." + create-dmg \ + --volname "${APP_NAME}" \ + --volicon "$ICON" \ + --window-pos 200 120 \ + --window-size 600 400 \ + --icon-size 100 \ + "${APP_NAME}.app" 180 185 \ + --hide-extension "${APP_NAME}.app" \ + --app-drop-link 420 185 \ + --background "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" \ + "$DMG_OUT" +else + echo "使用 hdiutil 生成 DMG..." + # hdiutil fallback — 创建一个简单的 DMG + TMP_DMG="$DIST_DIR/tmp.dmg" + MOUNT_POINT="/Volumes/${APP_NAME}" + + # 创建临时 DMG,足够放下 .app + APP_SIZE=$(du -sh "$APP" | cut -f1) + # 计算大小(MB),最少 50MB + SIZE_MB=$(du -sm "$APP" | cut -f1) + SIZE_MB=$((SIZE_MB < 50 ? 50 : SIZE_MB + 20)) + + hdiutil create -size "${SIZE_MB}m" -fs HFS+ -volname "${APP_NAME}" "$TMP_DMG" + hdiutil attach "$TMP_DMG" -nobrowse -mountpoint "$MOUNT_POINT" + + # 复制 .app 到 DMG + cp -R "$APP" "$MOUNT_POINT/" + + # 创建 Applications 快捷方式 + ln -s /Applications "$MOUNT_POINT/Applications" + + # 设置 DMG 布局(AppleScript) + osascript << APPLESCRIPT +tell application "Finder" + tell disk "${APP_NAME}" + open + set current view of container window to icon view + set toolbar visible of container window to false + set statusbar visible of container window to false + set bounds of container window to {400, 100, 1000, 500} + set viewOptions to icon view options of container window + set arrangement of viewOptions to not arranged + set icon size of viewOptions to 100 + set position of item "${APP_NAME}.app" of container window to {150, 200} + set position of item "Applications" of container window to {450, 200} + close + end tell +end tell +APPLESCRIPT + + hdiutil detach "$MOUNT_POINT" + hdiutil convert "$TMP_DMG" -format UDZO -o "$DMG_OUT" + rm -f "$TMP_DMG" +fi + +echo "✅ DMG 创建完成: $DMG_OUT" +echo "" +echo "输出文件:" +ls -lh "$DMG_OUT" From 2e51c08ef32a2226dd32ce3026cbca1feff90f07 Mon Sep 17 00:00:00 2001 From: carl chen Date: Mon, 12 Oct 2026 15:54:00 +0800 Subject: [PATCH 2/6] =?UTF-8?q?fix(ci):=20=E4=BF=AE=E5=A4=8D=20create-dmg?= =?UTF-8?q?=20=E8=B0=83=E7=94=A8=E5=8F=82=E6=95=B0=EF=BC=8C=E8=A1=A5?= =?UTF-8?q?=E4=B8=8A=20--icon=20=E5=89=8D=E7=BC=80=E5=92=8C=E6=BA=90?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- scripts/build-macos-app.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/build-macos-app.sh b/scripts/build-macos-app.sh index e681ae9..f8a08cc 100755 --- a/scripts/build-macos-app.sh +++ b/scripts/build-macos-app.sh @@ -91,11 +91,12 @@ if command -v create-dmg &>/dev/null; then --window-pos 200 120 \ --window-size 600 400 \ --icon-size 100 \ - "${APP_NAME}.app" 180 185 \ + --icon "${APP_NAME}.app" 180 185 \ --hide-extension "${APP_NAME}.app" \ --app-drop-link 420 185 \ --background "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" \ - "$DMG_OUT" + "$DMG_OUT" \ + "$STAGE" else echo "使用 hdiutil 生成 DMG..." # hdiutil fallback — 创建一个简单的 DMG From 819389d3dab35c20ae27a44f810e80528bf1be07 Mon Sep 17 00:00:00 2001 From: carl chen Date: Mon, 12 Oct 2026 16:16:39 +0800 Subject: [PATCH 3/6] =?UTF-8?q?fix(ci):=20=E5=8E=BB=E6=8E=89=20create-dmg?= =?UTF-8?q?=20=E7=9A=84=20--background=20data=20URI=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit create-dmg 的 --background 只接受真实文件路径,不支持 data URI。 去掉该参数,DMG 使用默认白色背景。 Co-Authored-By: Claude Code --- scripts/build-macos-app.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/build-macos-app.sh b/scripts/build-macos-app.sh index f8a08cc..a8207ae 100755 --- a/scripts/build-macos-app.sh +++ b/scripts/build-macos-app.sh @@ -94,7 +94,6 @@ if command -v create-dmg &>/dev/null; then --icon "${APP_NAME}.app" 180 185 \ --hide-extension "${APP_NAME}.app" \ --app-drop-link 420 185 \ - --background "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" \ "$DMG_OUT" \ "$STAGE" else From 823730e25da8cfe09efe200e3f3b8c1c79f7e7ae Mon Sep 17 00:00:00 2001 From: carl chen Date: Mon, 12 Oct 2026 16:53:42 +0800 Subject: [PATCH 4/6] fix(ci): build release from triggering commit --- .github/workflows/release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7204025..45f5269 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: inputs: tag: - description: 'Release tag to publish (for example, v0.1.2)' + description: 'Release tag to publish from the selected branch/tag (must match package.json version)' required: true type: string @@ -40,7 +40,8 @@ jobs: - name: Check out source uses: actions/checkout@v7 with: - ref: ${{ github.ref_type == 'tag' && github.ref_name || inputs.tag }} + # Build the triggering commit so manual runs include fixes from the selected ref. + ref: ${{ github.sha }} - name: Set up pnpm uses: pnpm/action-setup@v6 From 8db7eaa2fcc53df47b16e1dc5008634df64129b8 Mon Sep 17 00:00:00 2001 From: carl chen Date: Mon, 12 Oct 2026 18:07:54 +0800 Subject: [PATCH 5/6] feat: release v0.2.1 with signed automatic updates and Magpie support --- .github/workflows/release.yml | 38 +++++++- CHANGELOG.md | 10 ++ README.md | 2 + README.zh-CN.md | 2 + app/package.json | 4 +- app/src/App.vue | 12 +-- app/src/chat/useChat.ts | 2 +- app/src/components.d.ts | 3 + app/src/components/DocumentTitle.vue | 78 ++++++++++++++++ app/src/components/ProviderSettings.vue | 117 ++++++++++++++---------- app/src/components/SettingsModal.vue | 7 +- app/src/components/UpdateSettings.vue | 81 ++++++++++++++++ app/src/composables/providerPresets.ts | 92 +++---------------- app/src/platform/mygoBridge.ts | 4 + app/src/styles/base.css | 20 ++++ app/vite.config.ts | 10 +- native/e2e/assertions.js | 88 ++++++++++++++++++ native/main.go | 16 ++++ native/mygo.config.ts | 5 +- native/services/ai.go | 90 +++++++++++++++--- native/services/ai_test.go | 69 ++++++++++++++ native/services/updates.go | 36 ++++++++ package.json | 4 +- resources/update-public-key.txt | 1 + scripts/build-native.mjs | 21 +++++ scripts/package-update.mjs | 34 +++++++ 26 files changed, 683 insertions(+), 163 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 app/src/components/DocumentTitle.vue create mode 100644 app/src/components/UpdateSettings.vue create mode 100644 native/services/ai_test.go create mode 100644 native/services/updates.go create mode 100644 resources/update-public-key.txt create mode 100644 scripts/build-native.mjs create mode 100644 scripts/package-update.mjs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 45f5269..808e366 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,12 +29,15 @@ jobs: - os: macos-latest artifact: macos output: Muse + target: darwin-arm64 - os: windows-latest artifact: windows output: Muse.exe + target: windows-amd64 - os: ubuntu-latest artifact: linux output: Muse-linux + target: linux-amd64 steps: - name: Check out source @@ -74,6 +77,9 @@ jobs: - name: Build frontend run: pnpm run build:web + - name: Typecheck frontend + run: pnpm run typecheck + - name: Lint (gofmt) working-directory: native shell: bash @@ -83,11 +89,15 @@ jobs: working-directory: native run: go vet ./... - - name: Build binary + - name: Test native services working-directory: native + run: go test ./... + + - name: Build binary env: - CGO_ENABLED: '0' - run: go build -ldflags="-s -w" -o ${{ matrix.output }} . + GOOS: ${{ matrix.artifact == 'macos' && 'darwin' || matrix.artifact }} + GOARCH: ${{ matrix.artifact == 'macos' && 'arm64' || 'amd64' }} + run: node scripts/build-native.mjs ${{ matrix.output }} - name: Compress with UPX (Linux/Windows) if: matrix.os != 'macos-latest' @@ -116,6 +126,27 @@ jobs: chmod +x scripts/build-macos-app.sh scripts/build-macos-app.sh "native/${{ matrix.output }}" "resources/icon.icns" "${RELEASE_TAG#v}" + - name: Sign automatic update + shell: bash + env: + MYGO_UPDATER_PRIVATE_KEY: ${{ secrets.MYGO_UPDATER_PRIVATE_KEY }} + run: | + if [ "$RUNNER_OS" = "macOS" ]; then + node scripts/package-update.mjs dist/stage Muse.app "${{ matrix.target }}" + else + node scripts/package-update.mjs native "${{ matrix.output }}" "${{ matrix.target }}" + fi + + - name: Upload signed update + uses: actions/upload-artifact@v7 + with: + name: ${{ matrix.artifact }}-update + path: | + dist/Muse-*.tar.gz + dist/update-*.json + if-no-files-found: error + retention-days: 7 + - name: Upload binary (Linux/Windows) if: matrix.os != 'macos-latest' uses: actions/upload-artifact@v7 @@ -151,4 +182,5 @@ jobs: tag_name: ${{ github.ref_type == 'tag' && github.ref_name || inputs.tag }} name: Muse ${{ github.ref_type == 'tag' && github.ref_name || inputs.tag }} generate_release_notes: true + make_latest: true files: release-assets/* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..513b74c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +## 0.2.1 + +- Add signed automatic updates: daily background checks, release notes, download progress, install and restart, and optional automatic downloads. / 新增签名自动更新:每天后台检查,查看更新说明、下载进度、安装并重启,可选择自动下载安装。 +- Add Magpie local gateway preset and discover models through `/v1/models`, preserving custom model IDs on refresh. / 新增 Magpie 本地网关预设,自动获取模型,刷新时保留自定义 ID。 +- Wrap long document titles and tokens, widen the responsive editor, and keep tables within the page. / 修复长标题截断,优化正文宽度、长链接与表格换行。 +- Load the Muse logo explicitly in the Dock. / Dock 显式加载 Muse Logo。 + +Install 0.2.1 manually once if upgrading from 0.2.0; future versions can update in the app. macOS builds support Apple Silicon; Windows and Linux builds support x64. / 从 0.2.0 升级需手动安装一次 0.2.1,后续可在应用内更新。macOS 支持 Apple Silicon,Windows 与 Linux 支持 x64。 diff --git a/README.md b/README.md index a900dbc..8f6f52b 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ * **AI tool calling (agent)** — describe a document problem in natural language and the AI edits the document directly through tools (read / replace selection / insert at caret / append to end / replace all); ⌘Z undoes it. Selecting text reveals a quick action bar: Polish / Expand / Summarize / Translate * **Find & replace** — ⌘F to open, ⌘G / ⇧⌘G for prev/next, highlighted matches * **Word count** — live stats in the status bar +* **Automatic updates (0.2.1+)** — signed daily update checks, in-app download/install and restart; Settings > App Updates controls automatic checks/downloads. Install 0.2.1 manually once when upgrading from 0.2.0. + * **Settings panel** — font size / line height, applied instantly and persisted * **File management** — open / save / save as / new via native menus and shortcuts (⌘N / ⌘O / ⌘S / ⌘⇧S) * Drag & drop to open files diff --git a/README.zh-CN.md b/README.zh-CN.md index 0a065ba..08ce22f 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -29,6 +29,8 @@ 一个**所见即所得(WYSIWYG)的 Markdown 桌面编辑器**:打字时 `#` 立刻变成标题、`**加粗**` 即时生效、代码块用 **Shiki** 实时语法高亮。基于 **Milkdown 7**(ProseMirror)构建,未来计划接入 **AI 流式输出**(Muse 的真正主线)。 +* **自动更新(0.2.1 起)** — 每天后台检查、签名验证、应用内下载安装并重启;设置 → 应用更新可控制自动检查和自动下载。从 0.2.0 升级需手动安装一次 0.2.1。 + ## ✨ 功能特性 * **所见即所得** — Milkdown 7 内核,边打字边渲染 Markdown(标题 / 加粗 / 列表 / 引用 / 表格 / 任务列表等) diff --git a/app/package.json b/app/package.json index 4df6d8b..ff6df81 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "@muse/app", - "version": "0.1.2", + "version": "0.2.1", "private": true, "description": "Muse · Typora 式所见即所得 Markdown 编辑器(Shiki 高亮 + Milkdown 7)", "license": "MIT", @@ -49,4 +49,4 @@ "vue": "^3.5.0", "vue-tsc": "^3.0.0" } -} \ No newline at end of file +} diff --git a/app/src/App.vue b/app/src/App.vue index 752346e..427459d 100644 --- a/app/src/App.vue +++ b/app/src/App.vue @@ -6,6 +6,7 @@ import { XProvider } from '@antdv-next/x' import type { XProviderProps } from '@antdv-next/x' import MilkdownEditor from './editor/MilkdownEditor.vue' import TitleBar from './components/TitleBar.vue' +import DocumentTitle from './components/DocumentTitle.vue' import SidePanel from './components/SidePanel.vue' import OutlinePanel from './components/OutlinePanel.vue' import ChatPanel from './chat/ChatPanel.vue' @@ -283,7 +284,7 @@ function onTreeRenamed(e: Event): void { const showSettings = ref(false) const recentFiles = ref([]) -const titleInputRef = ref(null) +const titleInputRef = ref | null>(null) function focusTitleInput(): void { titleInputRef.value?.focus() titleInputRef.value?.select() @@ -754,15 +755,12 @@ const getDocContext = (): string => fullContent.value :class="{ 'focus-mode': viewMode.focus }" @scroll.passive="onEditorScroll" > -
- +
diff --git a/app/src/chat/useChat.ts b/app/src/chat/useChat.ts index 27a002c..00a05d9 100644 --- a/app/src/chat/useChat.ts +++ b/app/src/chat/useChat.ts @@ -139,7 +139,7 @@ export function useChat() { (p) => { setChatConnection(p ? { baseUrl: p.baseUrl ?? '', apiKey: p.apiKey } : null) }, - { immediate: true } + { immediate: true, deep: true } ) // ===== Provider(真实请求走主进程 IPC;无供应商时回退本地 mock)===== diff --git a/app/src/components.d.ts b/app/src/components.d.ts index 4458a06..a61726a 100644 --- a/app/src/components.d.ts +++ b/app/src/components.d.ts @@ -26,9 +26,11 @@ declare module 'vue' { ARow: typeof import('antdv-next')['Row'] ASegmented: typeof import('antdv-next')['Segmented'] ASlider: typeof import('antdv-next')['Slider'] + ASwitch: typeof import('antdv-next')['Switch'] ATag: typeof import('antdv-next')['Tag'] ATooltip: typeof import('antdv-next')['Tooltip'] ATypographyText: typeof import('antdv-next')['TypographyText'] + DocumentTitle: typeof import('./components/DocumentTitle.vue')['default'] EditorContextMenu: typeof import('./components/EditorContextMenu.vue')['default'] EntryScreen: typeof import('./components/EntryScreen.vue')['default'] FileTreePanel: typeof import('./components/FileTreePanel.vue')['default'] @@ -42,5 +44,6 @@ declare module 'vue' { SourceEditor: typeof import('./components/SourceEditor.vue')['default'] StatusBar: typeof import('./components/StatusBar.vue')['default'] TitleBar: typeof import('./components/TitleBar.vue')['default'] + UpdateSettings: typeof import('./components/UpdateSettings.vue')['default'] } } diff --git a/app/src/components/DocumentTitle.vue b/app/src/components/DocumentTitle.vue new file mode 100644 index 0000000..de2f7b0 --- /dev/null +++ b/app/src/components/DocumentTitle.vue @@ -0,0 +1,78 @@ + + +