Murasaki
ガイド

ネイティブ API

現時点で、Murasaki のネイティブサーフェスからアプリに公開されているもの。

Murasaki のネイティブウィンドウ、メニュー、OS 統合は @murasakijs/native — 自社製の Rust バインディング(tao/wry/muda)— によって支えられているため、Rust を書く ことは一切ありません。このページでは、あなたの React アプリから実際に到達可能なも のを説明します。

ウィンドウとネイティブメニューバー

windowのshape(サイズ、title、route、表示、permission、macOS vibrancy)は murasaki.config.ts で宣言します。window はprimaryのmainwindows はlabel付き secondaryです。全体像はウィンドウと権限を参照してください。

macOS では、標準的な App/Edit/Window メニューバー(About、Services、Hide、Quit、 Edit、Window)が自動的に生成され、config.locales からローカライズされます — コード は不要です。

コンテキストメニュー

右クリックのコンテキストメニューは、マークアップではなくフックである useContextMenu で宣言し、Rust 側に投げられて macOS / Windows では本物の NSMenu / HMENU がポップアップします。詳しくは専用の コンテキストメニューガイドを参照してください。

ダイアログ、クリップボード、通知、shell、ウィンドウ操作

レンダラーから安全に使えるネイティブ機能は murasaki/native から import します。 すべて Promise ベースで、リクエストID付きIPCをRustホストが直接処理します。

'use client'

import { app, appWindow, clipboard, dialog, notification, shell, systemPermission, tray, windows } from 'murasaki/native'

const files = await dialog.openFile({
  multiple: true,
  filters: [{ name: 'Images', extensions: ['png', 'jpg'] }],
})
await clipboard.writeText(files.join('\n'))
await notification.show({ title: '選択完了', body: `${files.length} files` })
await appWindow.setTitle('Import complete')
console.log(await appWindow.getLabel())
await windows.open('preview')
await shell.showItemInFolder(files[0])
API操作
appquit(gracefulなapplication shutdown)
dialogopenFile, openDirectory, saveFile
clipboardreadText, writeText
notificationshow
shellopenExternal(安全なURLスキームのみ)、showItemInFolder
systemPermissionmacOSのcamera / microphone / screen recording / accessibility向けstatus, request
appWindowgetLabel, setTitle, setSize, minimize, toggleMaximize, show, hide, focus, close, setAlwaysOnTop、状態取得
windows宣言label向けopen, list, show, hide, focus, close
traycreate, remove, setTooltip, setIcon, setMenu, onClick, onMenuItem

app.quit() とrootの quit() helperはいずれも app:quit が必要です。権限のない secondary rendererからapplication全体を終了することはできません。

0.50 migration: 以前のquit()はpermissionなしのraw IPCを送っていました。 既存のnon-updater appで使用しているwindowにはapp:quitを追加してください。 built-in updaterを有効にした場合だけ、検証済みinstall / restart handshakeの後方互換性を 保つためprimary windowへ自動付与されます。

これらは src/main.ts から直接呼ぶAPIではなく、信頼済みレンダラー向けです。 ブリッジはアプリと完全一致するoriginだけを受け付け、固定allowlistのコマンドだけを 公開します。各rendererが使うcommandだけを window.capabilities / windows[label].capabilities で許可してください。argument / path / URL scopeは 計画中なので、権限を持つwindowに信頼できないremote contentを読み込まないでください。

トレイアイコン

client componentから1つのsystem tray iconを作成できます。既定では config.icon を使い、明示指定する場合は8-bit RGB/RGBA PNGを渡します。 操作ごとに独立したpermissionが必要です。

import { tray } from 'murasaki/native'

await tray.create({
  tooltip: '同期中',
  template: true,
  menu: [
    { id: 'open', label: 'Murasakiを開く' },
    { separator: true },
    { id: 'quit', label: '終了' },
  ],
  menuOnLeftClick: navigator.userAgent.includes('Mac OS X'),
  menuOnRightClick: true,
})
const unsubscribe = tray.onClick(({ button, double }) => {
  console.log({ button, double })
})
const unsubscribeMenu = tray.onMenuItem(async (id) => {
  if (id === 'open') await appWindow.show()
  if (id === 'quit') await app.quit()
})

await tray.setTooltip('同期完了')
await tray.setIcon('/absolute/path/to/synced.png')
await tray.setMenu([{ id: 'open', label: 'Murasakiを開く' }])
// 後で: unsubscribe(); unsubscribeMenu(); await tray.remove()
murasaki.config.ts
export default defineConfig({
  // ...
  capabilities: [
    'tray:create',
    'tray:setTooltip',
    'tray:setIcon',
    'tray:setMenu',
    'tray:remove',
    'window:show',
    'app:quit',
  ],
})

template はmacOS専用です。もう一度 create すると既存アイコンを置き換えます。 tray menuはevent駆動で、click可能な項目には一意なidが必要です。quitなどのprivileged actionは、それぞれ本来のnative capabilityを通過します。process-wide iconを作成した rendererを閉じるとiconも削除されます。global shortcutとLinux trayは未対応です。

システム権限

OSの同意とMurasaki renderer capabilityは別物です。packaged macOS appでは、用途説明と 任意の起動時requestをconfigに宣言できます。用途説明はInfo.plistへ書き込まれ、camera / microphoneの説明が欠けた状態でpromptしてappがcrashすることを防ぎます。

murasaki.config.ts
export default defineConfig({
  // ...
  systemPermissions: {
    macOS: {
      camera: {
        usageDescription: 'ビデオ通話でカメラを使用します。',
        requestOnLaunch: true,
      },
      microphone: {
        usageDescription: '音声通話でマイクを使用します。',
      },
      screenRecording: { requestOnLaunch: false },
      accessibility: { requestOnLaunch: false },
    },
  },
  capabilities: [
    'systemPermission:status',
    'systemPermission:request',
  ],
})

client componentから文脈に合わせて要求する場合:

const status = await systemPermission.status('microphone')
if (status === 'notDetermined') {
  await systemPermission.request('microphone')
}

camera / microphoneのOS promptは非同期で完了するため、request()が最初は notDeterminedを返す場合があります。protected featureを有効にする前に、appが再び focusされた時点でstatus()を再確認してください。

requestOnLaunchはpackaged macOS appに適用されます。developmentはterminal / Node hostの identityで動くため、TCCの実動作はpackage buildから確認してください。Windowsのunpackaged desktop appではcamera / microphoneを利用するdevice API側が同意を要求し、汎用的な起動時 promptはありません。そのためMurasakiは権限取得を装わずunsupportedを返します。macOSの screen recording / accessibilityは初回とdenyを区別できない場合notGrantedです。

組み込みのメニューアクション

項目の action は、関数の代わりに組み込みの <Action.*/> 要素の1つにできます — これらは(OS 自身が処理する)ネイティブロール、または小さなクライアントサイドの挙動を 実行します:

Action挙動
<Action.Copy />, <Action.Paste />, <Action.Cut />, <Action.SelectAll />, <Action.Undo />, <Action.Redo />ネイティブ OS の編集ロール
<Action.Quit />ネイティブの終了ロール
<Action.Reload />ウィンドウをリロードします(location.reload()
<Action.Navigate to="/path" />ルーター経由のクライアントサイドナビゲーション
<Action.Run action={fn} />通常の関数を実行します(関数を直接渡すのと同じです)

自動更新

useUpdate() は、更新の確認・ダウンロード・インストールを行います — その確認/ダウン ロードのロジックは Node 上で動作し、コンテキストメニューやアプリメニューが使う IPC ブリッジではなく、アプリの他の部分を配信しているのと同じローカル HTTP サーバー(Server Actions や API Routes と同じ仕組み)経由でアクセスされます。<UpdateButton /> (こちらも murasaki から、@murasakijs/ui でスタイリング)は、それをラップした すぐに使えるボタンです:

import { useUpdate } from 'murasaki'

const { status, latest, check, download, install } = useUpdate()

statusidle → checking → available → downloading → ready(または not-available / error)と遷移します。セットアップはコマンド2つだけです — マニフェ ストの形式、セキュリティモデル、GitHub Actions でのリリースワークフローについては、専 用の自動更新ガイドを参照してください。

global shortcut、argument / path単位のcapability ruleはまだ公開されていません。 設計前に プラットフォーム機能状況を確認してください。

次へ

GitHub でこのページを改善

On this page