From a05c9faef1421e8e6b28797df8d715f862a94391 Mon Sep 17 00:00:00 2001 From: tkym <42635181+Htkym@users.noreply.github.com> Date: Sun, 4 Oct 2026 17:05:39 +0900 Subject: [PATCH 1/2] Fix character groups and details, prepare Plotree 1.0.5 Share tree and graph memberships, improve relationship and overlapping group selection, preserve positions, and add independent card character display. Constrain menus and defer graph rendering around pointer gestures and unload. Update bilingual user documentation and cover the behavior with regressions. --- .agents/skills/plotree-store-release/SKILL.md | 103 ++ .../scripts/Test-ReleaseArtifacts.ps1 | 137 ++ README.ja.md | 35 +- README.md | 24 +- RELEASE_NOTES.md | 27 + site/content/user-manual-en.md | 16 + site/content/user-manual-ja.md | 16 + src/Plotree/MainPage.xaml | 199 ++- src/Plotree/MainPage.xaml.cs | 74 +- src/Plotree/Models/Character.cs | 13 + src/Plotree/Models/CharacterGroup.cs | 6 + src/Plotree/Models/CharacterRelationship.cs | 19 + src/Plotree/Models/NodeAppearance.cs | 6 +- src/Plotree/Models/PlotProject.cs | 4 +- src/Plotree/Package.appxmanifest | 2 +- src/Plotree/Plotree.csproj | 3 + src/Plotree/Services/AppearanceResolver.cs | 6 +- .../Services/CharacterGraphInteraction.cs | 65 + src/Plotree/Services/CharacterGraphLayout.cs | 124 ++ .../Services/CharacterSummaryFormatter.cs | 24 + src/Plotree/Services/DeferredGraphRenderer.cs | 63 + .../Services/MenuPlacementCalculator.cs | 21 + .../Services/NodeTextLayoutCalculator.cs | 11 +- src/Plotree/Services/ProjectSerializer.cs | 92 +- src/Plotree/Services/SvgExportService.cs | 26 +- src/Plotree/Strings/en-US/Resources.resw | 54 + src/Plotree/Strings/ja-JP/Resources.resw | 54 + src/Plotree/ViewModels/MainPageViewModel.cs | 329 +++- src/Plotree/ViewModels/NodeViewModel.cs | 4 + src/Plotree/Views/CharacterGraph.xaml | 98 ++ src/Plotree/Views/CharacterGraph.xaml.cs | 1470 +++++++++++++++++ src/Plotree/Views/InAppFlyout.cs | 118 ++ src/Plotree/Views/NodeCard.xaml | 12 +- src/Plotree/Views/NodeCard.xaml.cs | 18 +- .../CharacterGraphInteractionTests.cs | 279 ++++ tests/Plotree.Tests/CharacterGraphTests.cs | 261 +++ .../CharacterGroupIsolationTests.cs | 110 ++ .../DeferredGraphRendererTests.cs | 95 ++ tests/Plotree.Tests/MainPageViewModelTests.cs | 73 + .../MenuPlacementCalculatorTests.cs | 41 + tests/Plotree.Tests/ProjectSerializerTests.cs | 52 + tests/Plotree.Tests/SvgExportServiceTests.cs | 20 + 42 files changed, 4038 insertions(+), 166 deletions(-) create mode 100644 .agents/skills/plotree-store-release/SKILL.md create mode 100644 .agents/skills/plotree-store-release/scripts/Test-ReleaseArtifacts.ps1 create mode 100644 src/Plotree/Models/CharacterRelationship.cs create mode 100644 src/Plotree/Services/CharacterGraphInteraction.cs create mode 100644 src/Plotree/Services/CharacterGraphLayout.cs create mode 100644 src/Plotree/Services/CharacterSummaryFormatter.cs create mode 100644 src/Plotree/Services/DeferredGraphRenderer.cs create mode 100644 src/Plotree/Services/MenuPlacementCalculator.cs create mode 100644 src/Plotree/Views/CharacterGraph.xaml create mode 100644 src/Plotree/Views/CharacterGraph.xaml.cs create mode 100644 src/Plotree/Views/InAppFlyout.cs create mode 100644 tests/Plotree.Tests/CharacterGraphInteractionTests.cs create mode 100644 tests/Plotree.Tests/CharacterGraphTests.cs create mode 100644 tests/Plotree.Tests/CharacterGroupIsolationTests.cs create mode 100644 tests/Plotree.Tests/DeferredGraphRendererTests.cs create mode 100644 tests/Plotree.Tests/MenuPlacementCalculatorTests.cs diff --git a/.agents/skills/plotree-store-release/SKILL.md b/.agents/skills/plotree-store-release/SKILL.md new file mode 100644 index 0000000..5dc001f --- /dev/null +++ b/.agents/skills/plotree-store-release/SKILL.md @@ -0,0 +1,103 @@ +--- +name: plotree-store-release +description: Prepare a versioned Plotree Microsoft Store upload and optional direct installer, verify release artifacts, and hand them to the user. Use for Plotree release preparation; publishing, uploading, tagging, and merging require their own authorization. +--- + +# Plotree Store release preparation + +Run from the existing repository root. Deliver verified local artifacts, their SHA256 hashes and source revision, and a clear handoff for the user to upload in Partner Center. Preserve existing working changes/index and other active sessions; create a work branch first if requested. Do not move the repository or clean unrelated files/processes. + +## Existing skills and responsibilities + +This repository skill owns Plotree's version metadata, current project configuration, bilingual user documentation, artifact comparison, direct-installer helper, and handoff. Generic WinUI/MSIX/certificate guidance belongs to the existing **winui-packaging** skill (catalog name may be **winui:winui-packaging**); build diagnosis belongs to **winui-dev-workflow**. Resolve these by name in the active skill catalog. They may come from a user installation or plugin cache, so do not hardcode a home directory or plugin version and do not duplicate their full instructions here. + +The optional user-local **winui-ui-testing** skill covers GUI testing only when requested. Release preparation does not authorize installing/running an app or changing certificate trust. If the generic skills are unavailable, the SDK-native commands and verification helper below support a standalone release workflow. No installation of another skill/tool is required merely to prepare artifacts. Do not edit/delete user-local skills to resolve overlap. + +For prose review, use available Antigravity/Yomiyasu when requested and supported. Inspect the actual resulting diff even if the tool was instructed to review without editing; retain only accurate authorized changes. Keep development instructions here and in contributor docs, outside end-user READMEs. + +## Establish the release input + +- Obtain the user's explicit four-part package version. Do not reuse a previous release's version; ask if a three-part request is ambiguous. Update `src/Plotree/Package.appxmanifest` Identity Version and `src/Plotree/Plotree.csproj` Version/AssemblyVersion/FileVersion consistently with the repository's convention (app Version uses three parts, package/assembly/file versions four). +- Read current `Package.appxmanifest`, `.csproj`, release notes, `.gitignore`, `scripts/New-DirectInstaller.ps1`, and any existing local publish profiles relevant to packaging. Preserve Store Identity Name/Publisher/PublisherDisplayName and compare them with a prior package; changing Store identity is a separate task. +- The current bundle targets **x64 and ARM64**, includes **en-US and ja-JP** resources in each architecture package, and is self-contained. Verify effective settings and actual output rather than assuming a publish profile exists on another contributor's machine. Both `.NET SelfContained` and `WindowsAppSDKSelfContained` matter; preserve existing trimming/ReadyToRun/AOT choices. +- Update JA/EN README and release notes for actual user features, installation/use and limitations. Check in-app instructions against localized defaults/UI names and file-format compatibility. Exclude build/test/work logs and implementation explanations from READMEs. Do not claim GUI behavior was tested when only code or unit tests were checked. +- On a shared PC, coordinate a sustained build/test window with active performance measurements before starting. Do not stop another session's jobs or alter its repository. Report when all CPU-heavy jobs have ended. + +## Prerequisites and final tests + +Use Windows, the .NET SDK required by the current target framework, restored project dependencies, and Windows SDK MSIX tools (`signtool.exe`, `makepri.exe` when inspecting packaged strings). Locate SDK tools from the installed Windows Kits; verify help/versions before using unfamiliar flags. The generic WinApp skills require CLI 0.7+; an older/missing CLI does not block the verified SDK-native route. Do not install or upgrade tooling without authorization. + +Run the full applicable suite after final code/resource/version edits: + +```powershell +dotnet test .\tests\Plotree.Tests\Plotree.Tests.csproj -p:Platform=x64 --logger trx --results-directory .\artifacts\release-tests +``` + +Use `--no-restore` only if the current configuration is already restored. Stop on test/build failures, resolve the cause within scope, and do not silently omit failing tests. Documentation/skill-only changes after a verified build do not require another binary build. Record the build's source hashes/revision; a later commit may additionally contain only documentation. + +## Reuse signing material securely + +Read the manifest Publisher and discover existing matching, unexpired signing certificates in `Cert:\CurrentUser\My` using public metadata (`Subject`, `Thumbprint`, `NotAfter`, `HasPrivateKey`). Compare a prior public `.cer` and signed bundle. Select the existing approved signer; if several candidates remain, resolve the ambiguity before signing. Do not create credentials/certificates or export private keys. + +Prefer the existing certificate store key. A configured certificate thumbprint allows overriding `PackageCertificateKeyFile` to empty and avoids reading a PFX/password. If a password-protected PFX is genuinely needed, locate the project's existing UserSecretsId and approved credential mechanism without displaying its contents. A missing ID is not permission to invent one or inspect arbitrary secret stores. Never place a password in a command line, transcript, build log, source, PR, or generated reference. Do not dump all MSBuild properties or environment variables. + +Certificate trust is separate from packaging. Never install a trusted root, modify security settings, or run the direct installer automatically to make signature policy verification pass. + +## SDK-native StoreUpload packaging + +Set `$releaseVersion` from the resolved user request, `$releasePackages` to the versioned directory below, and `$signingThumbprint` to the discovered existing signer. These are task variables, not persistent configuration: + +```powershell +$releasePackages = Join-Path (Get-Location) "src\Plotree\AppPackages\$releaseVersion\" +$publishArguments = @( + 'publish', '.\src\Plotree\Plotree.csproj', '--configuration', 'Release', + '-p:Platform=x64', '-p:GenerateAppxPackageOnBuild=true', '-p:AppxBundle=Always', + '-p:AppxBundlePlatforms=x64|ARM64', '-p:UapAppxPackageBuildMode=StoreUpload', + "-p:AppxPackageDir=$releasePackages", + "-p:PackageCertificateThumbprint=$signingThumbprint", + '-p:PackageCertificateKeyFile=', '-p:PackageCertificatePassword=' +) +dotnet @publishArguments +``` + +Inspect exit status and output paths. The project also sets a versioned AppxPackageTestDir from manifest identity. Verify the actual versioned folder rather than assuming all outputs followed a command-line directory override. + +If `mspdbcmf.exe` is missing, symbol generation can fail even though package files were produced. First check existing tools. Store analytics symbols are optional; when omitted, disclose the limitation and add `-p:AppxSymbolPackageEnabled=false` to the publish arguments. Warnings about the missing converter may still occur. Do not label a failed publish as successful merely because files exist; the adjusted publish must exit successfully. + +StoreUpload can produce an unsigned outer bundle even when architecture payloads were signed. Inspect the final bundle. If needed, use the discovered SDK tool and existing key: + +```powershell +& $signtool sign /fd SHA256 /s My /sha1 $signingThumbprint $releaseBundle +& $signtool verify /pa /v $releaseBundle +``` + +Use the established signing/timestamp policy; do not introduce a new service. Capture any verification failure accurately. A self-signed bundle can have valid cryptography while Windows policy reports an untrusted root. Check the exact error, compare history, and verify cryptography/content without changing trust; unrelated digest/signature errors are blockers. + +After signing, replace the single bundle entry in the generated `.msixupload` ZIP with the final signed bundle. Preserve any symbols entries; close/dispose the archive before verification. Compare embedded and standalone SHA256 hashes. SDK upload containers are ZIP files; never assume a container still contains the final bundle after external signing. + +For a requested direct installer, copy the matching existing **public** `.cer` into the versioned package directory, then use the established helper: + +```powershell +.\scripts\New-DirectInstaller.ps1 -PackageDirectory $releasePackages +``` + +The helper replaces its versioned staging/ZIP paths. Before running it, verify resolved targets are inside the intended artifacts directory, inspect any existing same-version output and preserve concurrent/user-owned work. Never delete old releases broadly. The ZIP must contain the two installer scripts, README, final signed bundle and matching public certificate. + +## Verify and hand off + +Run the read-only helper [scripts/Test-ReleaseArtifacts.ps1](scripts/Test-ReleaseArtifacts.ps1): + +```powershell +.\.agents\skills\plotree-store-release\scripts\Test-ReleaseArtifacts.ps1 ` + -Version $releaseVersion -VerifyDirectInstaller +``` + +It checks manifest identity/version, x64/ARM64 payloads and both languages, self-contained files, CMS signature, all payload/bundle block-map hashes, the upload's embedded bundle, and optional direct ZIP/certificate. CMS checking alone is not a substitute for Windows SIP/policy verification or Store certification; report those checks separately. Inspect packaged PRI resources with `makepri dump` when localized copy changed; verify both architectures actually contain the final wording. + +Record artifact paths, lengths, SHA256, source revision and any documentation-only difference from build inputs. Keep logs, TRX and receipts in ignored/private output, not in the PR. Review staged paths explicitly for source/docs/tests only; do not include generated packages, certificates, credentials or private story files. + +Commit/push/create a Draft PR only when authorized. Use the repository PR template and record tests, packaging, manual-only checks and known unresolved observations. Verify the remote PR is a draft and its head SHA matches the pushed commit. Preparation is not authorization to upload to Partner Center, create a GitHub release/tag, publish, or merge. + +Handoff the precise `.msixupload` (and direct ZIP if requested), hashes, Draft PR URL, and remaining manual checks. The user uploads to Microsoft Store. Do not guarantee Store acceptance or claim ARM64 runtime/GUI/packaged installation testing that was not performed. + +Primary references: [SDK packaging](https://learn.microsoft.com/en-us/windows/uwp/packaging/auto-build-package-uwp-apps), [upload container and optional symbols](https://learn.microsoft.com/en-us/windows/msix/package/packaging-uwp-apps), [Partner Center package upload](https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/msix/upload-app-packages). diff --git a/.agents/skills/plotree-store-release/scripts/Test-ReleaseArtifacts.ps1 b/.agents/skills/plotree-store-release/scripts/Test-ReleaseArtifacts.ps1 new file mode 100644 index 0000000..92d8096 --- /dev/null +++ b/.agents/skills/plotree-store-release/scripts/Test-ReleaseArtifacts.ps1 @@ -0,0 +1,137 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory)][ValidatePattern('^\d+\.\d+\.\d+\.\d+$')][string] $Version, + [string] $RepositoryRoot = (Join-Path $PSScriptRoot '../../../..'), + [switch] $VerifyDirectInstaller +) +$ErrorActionPreference = 'Stop' +Add-Type -AssemblyName System.IO.Compression.FileSystem +Add-Type -AssemblyName System.Security +$repo = (Resolve-Path -LiteralPath $RepositoryRoot).Path +[xml]$sourceManifest = Get-Content -LiteralPath (Join-Path $repo 'src/Plotree/Package.appxmanifest') -Raw +$expected = $sourceManifest.Package.Identity +if ($expected.Version -ne $Version) { throw 'Requested version differs from the source manifest.' } +$packageDir = Join-Path $repo "src/Plotree/AppPackages/$Version" +$bundles = @(Get-ChildItem -LiteralPath $packageDir -Filter '*.msixbundle' -File) +$uploads = @(Get-ChildItem -LiteralPath $packageDir -Filter '*.msixupload' -File) +if ($bundles.Count -ne 1 -or $uploads.Count -ne 1) { throw 'Expected exactly one bundle and one Store upload container.' } +$bundle = $bundles[0] +$upload = $uploads[0] +function Read-ZipXml($zip, $name) { + $entry = $zip.GetEntry($name) + if ($null -eq $entry) { throw "Missing XML entry: $name" } + $reader = [System.IO.StreamReader]::new($entry.Open()) + try { [xml]$reader.ReadToEnd() } finally { $reader.Dispose() } +} +function Get-EntryHash($entry) { + $stream = $entry.Open() + $sha = [System.Security.Cryptography.SHA256]::Create() + try { [BitConverter]::ToString($sha.ComputeHash($stream)).Replace('-', '') } + finally { $stream.Dispose(); $sha.Dispose() } +} +function Test-BlockMap($zip) { + $map = Read-ZipXml $zip 'AppxBlockMap.xml' + if ($map.BlockMap.HashMethod -ne 'http://www.w3.org/2001/04/xmlenc#sha256') { throw 'Unsupported block-map hash method.' } + $sha = [System.Security.Cryptography.SHA256]::Create() + $files = 0; $blocks = 0 + try { + foreach ($file in $map.BlockMap.File) { + $name = $file.Name.Replace('\', '/') + $entry = $zip.GetEntry($name) + if ($null -eq $entry -or $entry.Length -ne [long]$file.Size) { throw "Block-map entry/size mismatch: $name" } + $stream = $entry.Open() + try { + $buffer = New-Object byte[] 65536 + foreach ($block in $file.Block) { + $count = 0 + while ($count -lt $buffer.Length) { + $read = $stream.Read($buffer, $count, $buffer.Length - $count) + if ($read -eq 0) { break } + $count += $read + } + if ([Convert]::ToBase64String($sha.ComputeHash($buffer, 0, $count)) -ne $block.Hash) { throw "Block-map digest mismatch: $name" } + $blocks++ + } + if ($stream.ReadByte() -ne -1) { throw "Unverified payload bytes: $name" } + } finally { $stream.Dispose() } + $files++ + } + } finally { $sha.Dispose() } + [pscustomobject]@{ Files = $files; Blocks = $blocks; AllSHA256Valid = $true } +} +$zip = [System.IO.Compression.ZipFile]::OpenRead($bundle.FullName) +try { + $signatureEntry = $zip.GetEntry('AppxSignature.p7x') + if ($null -eq $signatureEntry) { throw 'The outer bundle is unsigned.' } + $stream = $signatureEntry.Open(); $memory = [System.IO.MemoryStream]::new() + try { $stream.CopyTo($memory); $bytes = $memory.ToArray() } finally { $stream.Dispose(); $memory.Dispose() } + if ($bytes.Length -lt 5 -or [System.Text.Encoding]::ASCII.GetString($bytes, 0, 4) -ne 'PKCX') { throw 'Unexpected MSIX signature header.' } + $cms = [System.Security.Cryptography.Pkcs.SignedCms]::new() + $cms.Decode($bytes[4..($bytes.Length - 1)]) + $cms.CheckSignature($true) + if ($cms.SignerInfos.Count -ne 1) { throw 'Unexpected bundle signer count.' } + $signer = $cms.SignerInfos[0].Certificate + if ($signer.Subject -ne $expected.Publisher) { throw 'Signing certificate subject differs from the manifest Publisher.' } + if ($signer.NotAfter -lt (Get-Date) -or $signer.NotBefore -gt (Get-Date)) { throw 'Signing certificate is outside its validity interval.' } + $manifest = Read-ZipXml $zip 'AppxMetadata/AppxBundleManifest.xml' + if ($manifest.Bundle.Identity.Name -ne $expected.Name -or $manifest.Bundle.Identity.Publisher -ne $expected.Publisher -or $manifest.Bundle.Identity.Version -ne $Version) { throw 'Bundle identity differs from source.' } + $packages = @() + foreach ($package in $manifest.Bundle.Packages.Package) { + if ($package.Type -ne 'application') { continue } + $entry = $zip.GetEntry($package.FileName) + if ($null -eq $entry -or $package.Version -ne $Version) { throw 'Missing or incorrectly versioned architecture payload.' } + $memory = [System.IO.MemoryStream]::new(); $stream = $entry.Open() + try { $stream.CopyTo($memory) } finally { $stream.Dispose() } + $memory.Position = 0 + $payload = [System.IO.Compression.ZipArchive]::new($memory, [System.IO.Compression.ZipArchiveMode]::Read) + try { + $app = Read-ZipXml $payload 'AppxManifest.xml' + $identity = $app.Package.Identity + if ($identity.Name -ne $expected.Name -or $identity.Publisher -ne $expected.Publisher -or $identity.Version -ne $Version -or $identity.ProcessorArchitecture -ne $package.Architecture) { throw 'Application payload identity mismatch.' } + $languages = @($app.Package.Resources.Resource | ForEach-Object Language | Sort-Object -Unique) + if ('en-US' -notin $languages -or 'ja-JP' -notin $languages) { throw 'Expected English/Japanese resources are absent.' } + $names = @($payload.Entries | ForEach-Object FullName) + foreach ($requiredFile in @('Plotree.exe', 'System.Private.CoreLib.dll', 'Microsoft.UI.Xaml.dll')) { + if (-not ($names | Where-Object { [System.IO.Path]::GetFileName($_) -eq $requiredFile })) { throw "Self-contained runtime file missing: $requiredFile" } + } + $packages += [pscustomobject]@{ Architecture = $package.Architecture; Version = $identity.Version; Languages = $languages; SelfContainedFilesPresent = $true; BlockMap = (Test-BlockMap $payload) } + } finally { $payload.Dispose(); $memory.Dispose() } + } + if (($packages.Architecture | Sort-Object) -join ',' -ne 'arm64,x64') { throw 'Expected exactly x64 and ARM64 application packages.' } + $bundleBlockMap = Test-BlockMap $zip +} finally { $zip.Dispose() } +$bundleHash = (Get-FileHash -LiteralPath $bundle.FullName -Algorithm SHA256).Hash +$zip = [System.IO.Compression.ZipFile]::OpenRead($upload.FullName) +try { + $entries = @($zip.Entries | Where-Object FullName -Like '*.msixbundle') + if ($entries.Count -ne 1 -or (Get-EntryHash $entries[0]) -ne $bundleHash) { throw 'Store upload does not contain the final signed bundle.' } + $uploadEntries = @($zip.Entries | ForEach-Object FullName) +} finally { $zip.Dispose() } +$artifacts = @($bundle.FullName, $upload.FullName) +$directVerified = $false +if ($VerifyDirectInstaller) { + $directPath = Join-Path $repo "artifacts/Plotree_${Version}_DirectInstaller.zip" + $zip = [System.IO.Compression.ZipFile]::OpenRead($directPath) + try { + $bundleEntries = @($zip.Entries | Where-Object FullName -Like '*.msixbundle') + $certEntries = @($zip.Entries | Where-Object FullName -Like '*.cer') + foreach ($requiredEntry in @('Install-Plotree.bat', 'Install-Plotree.ps1', 'README.txt')) { + if ($null -eq $zip.GetEntry($requiredEntry)) { throw "Missing direct-installer entry: $requiredEntry" } + } + if ($zip.Entries.Count -ne 5 -or $bundleEntries.Count -ne 1 -or $certEntries.Count -ne 1 -or (Get-EntryHash $bundleEntries[0]) -ne $bundleHash) { throw 'Unexpected direct-installer contents or bundle digest.' } + $stream = $certEntries[0].Open(); $memory = [System.IO.MemoryStream]::new() + try { $stream.CopyTo($memory); $publicCertificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($memory.ToArray()) } + finally { $stream.Dispose(); $memory.Dispose() } + try { if ($publicCertificate.Thumbprint -ne $signer.Thumbprint) { throw 'Direct-installer certificate differs from the signer.' } } + finally { $publicCertificate.Dispose() } + $directVerified = $true + } finally { $zip.Dispose() } + $artifacts += $directPath +} +[pscustomobject]@{ + Version = $Version; Identity = $expected.Name; Publisher = $expected.Publisher + CmsSignatureValid = $true; WindowsPolicyStatus = (Get-AuthenticodeSignature -LiteralPath $bundle.FullName).Status.ToString() + Packages = $packages; BundleBlockMap = $bundleBlockMap + UploadContainsFinalSignedBundle = $true; UploadEntries = $uploadEntries; DirectInstallerVerified = $directVerified + Artifacts = @($artifacts | ForEach-Object { [pscustomobject]@{ File = [System.IO.Path]::GetFileName($_); Bytes = (Get-Item -LiteralPath $_).Length; SHA256 = (Get-FileHash -LiteralPath $_ -Algorithm SHA256).Hash } }) +} diff --git a/README.ja.md b/README.ja.md index 52bb7e4..2468394 100644 --- a/README.ja.md +++ b/README.ja.md @@ -4,8 +4,6 @@ 小説・ゲーム創作向けのプロット作成ツールです。フローチャート形式のノードエディタで、選択肢やマルチエンディングを含む物語の流れを視覚的に設計できます。 -WinUI 3 (Windows App SDK) 製の Windows デスクトップアプリです。 - ![分岐ストーリーを設計するPlotreeのキャンバス](site/images/screenshots/01-overview-ja.png) ノード、選択肢、接続線を使って物語の流れを組み立て、タグ・登場人物・レイアウト・外観を一つのプロジェクトで管理できます。 @@ -36,40 +34,33 @@ GitHub 版は自己署名証明書を使用するため、初回インストー - **コピーと貼り付け** — Ctrl+C / Ctrl+V、上部ボタン、右クリックメニューから選択中のノードを複製。コピー対象のノード同士を結ぶ接続と、割り当て済みのタグ・登場人物・グループも維持 - **選択肢・マルチエンディング** — 選択肢ノードから複数の接続を張り、接続線に選択肢ラベルを付与可能 - **ハイブリッド自動レイアウト** — 階層レイアウトによる自動整列(左→右 / 上→下 切替可)。手動配置したノードはピン留めされ、自動整列の対象外に(「すべて再整列」でピン解除も可能) -- **ノード外観のカスタマイズ** — 種類(シーン / 選択肢 / エンディング)ごとにヘッダー色・幅・高さ・カード表示(タイトルのみ / タイトル+本文)をプロジェクト単位で設定。ノード個別に上書きすることも可能 +- **ノード外観のカスタマイズ** — 種類(シーン / 選択肢 / エンディング)ごとに、ヘッダー色・幅・高さ・カード表示(タイトルのみ / タイトル+本文)と、カードに登場人物を表示するかどうかをプロジェクト単位で設定。ノード個別に上書きすることも可能 - **複数タグ・登場人物・グループ管理** — ツールバーでカラータグと登場人物を選択中のすべてのノードへ一括追加・解除(混在状態も表示)。人物は複数グループに所属でき、人物ごとのグループ所属も確認・編集可能 -- **Undo / Redo と安全な編集履歴** — Ctrl+Z / Ctrl+Y またはツールバーから、ノード・接続・タグ・人物・外観・レイアウトの変更を取り消し / やり直し。テキスト入力とドラッグ / リサイズは 1 操作として扱います +- **Undo / Redo** — Ctrl+Z / Ctrl+Y またはツールバーから、ノード・接続・タグ・人物・外観・レイアウトの変更を取り消し / やり直し。テキスト入力とドラッグ / リサイズは 1 操作として扱います - **本文・メモ** — 各ノードにあらすじ本文と執筆メモを記録 -- **JSON 保存** — `.plotree` ファイル(中身は整形済み JSON)で保存。バージョンフィールド付きで将来のフォーマット移行にも対応 +- **プロジェクトの保存** — 物語と登場人物の情報を `.plotree` ファイルにまとめて保存 - **PNG / SVG / Markdown / テキストエクスポート** — グラフ全体を透明背景の `.png` / `.svg` に、起点からエンディングまでの全ルートを `.md` / `.txt` に書き出し。テキスト出力では行き止まりや到達不能ノードも警告として出力 - **日本語 / 英語 UI** — 設定メニューからいつでも切替(既定は OS の言語に従う) -## 動作環境 +## 人物相関図とグループ -- Windows 10 バージョン 1809 以降 / Windows 11 -- ビルドには .NET 10 SDK が必要 +「人物相関図」では、人物を配置して関係を線でつなげられます。新しく作った線のラベルは、最初は「関係名」です。線を選択すると「詳細」に関係が表示され、関係名とラベルの色を編集できます。選択中の人物は太い白枠で示され、関係の線はグループ背景に重なっていても選択できます。人物の画像には PNG / JPEG を使え、画像はプロジェクトと一緒に保存されます。 -## ビルドと実行 +人物は複数のグループに所属できます。所属はプロット画面と人物相関図で共有され、プロット画面で設定したグループも人物相関図の背景に反映されます。グループ分けや背景色を変えても、人物の位置は変わりません。同じグループの人物が近くにいるときは背景が1つにまとまり、離れた人物には個別の背景が表示されます。背景が重なるときは、背景とグループ名が階段状にずれて並び、選択したグループが手前に表示されます。背景を選択すると、「詳細」にグループ名と所属人物が表示され、背景のそばに編集メニューが開きます。背景の表示と非表示は、「グループ分け」の右隣にある「グループの表示」で切り替えられます。 -```powershell -git clone https://github.com/Htkym/Plotree.git -cd Plotree -dotnet build .\Plotree.slnx -p:Platform=x64 -``` +プロット画面の人物管理では、人物一覧の右側にグループ一覧が表示されます。人物の割り当て一覧は、グループごとに展開・折りたたみできます。「詳細」の登場人物は1人ずつ別の行にグループ名付きで表示され、文字を選択してコピーできます。カードに登場人物を表示するかどうかは、タイトル・本文の表示方法とは別に設定でき、種類ごとの既定値とノード個別の設定を選べます。初期状態では表示されません。この設定は PNG / SVG 出力にも反映されます。 -実行時は exe を直接起動せず、WinApp CLI の `winapp run`(または WinUI の BuildAndRun ワークフロー)を使用してください。 +## 動作環境 -`.plotree` ファイルのパスを引数に渡すと、そのプロジェクトを開いた状態で起動します。 +- Windows 10 バージョン 1809 以降 / Windows 11 -MSIX バンドルと `.cer` を作成した後、GitHub 配布用 ZIP は次のコマンドで生成できます。 +## 注意事項 -```powershell -.\scripts\New-DirectInstaller.ps1 -PackageDirectory .\src\Plotree\AppPackages\<バージョン> -``` +- Plotree 1.0.5 で保存した `.plotree` ファイルは、1.0.4 以前のバージョンでは開けません。 ## 基本操作 -詳しい手順と画面例は、[日本語版ユーザーマニュアル](site/content/user-manual-ja.md)を参照してください。 +詳しい手順と画面例は、[日本語版ユーザーマニュアル](https://htkym.github.io/Plotree/posts/user-manual-ja.html)を参照してください。 | 操作 | 方法 | |------|------| @@ -83,7 +74,7 @@ MSIX バンドルと `.cer` を作成した後、GitHub 配布用 ZIP は次の | コピー / 貼り付け | Ctrl+C / Ctrl+V、上部のコピー / 貼り付けボタン、または右クリックメニュー。選択ノード同士の接続もコピー | | 詳細編集 | ノードをクリックで右パネルに表示。ダブルクリックでタイトルにフォーカス(複数選択中は選択数と一括操作を表示) | | カラータグ | ツールバー「カラータグ」グループでタグを新規作成。チェックで選択中のすべてのノードへ追加、解除で削除(横線は一部のノードだけに付いた混在状態) | -| 登場人物・グループ | ツールバー「登場人物」の「割り当て」で選択中のすべてのノードに人物を追加 / 解除。「管理」から人物・グループを作成し、人物を複数グループへ所属させる | +| 登場人物・グループ | ツールバー「登場人物」の「割り当て」で選択中のすべてのノードに人物を追加 / 解除。「人物」「グループ」から人物とグループを作成し、人物を複数グループへ所属させる | | タグ・人物の確認 | 右パネルに、単一選択・複数選択のどちらでも割り当て済みタグと登場人物の要約を表示 | | ノード外観 | ツールバーの「外観」ボタン(設定 → ノードの外観…)で種類ごとの既定を設定。個別のノードは右パネル「外観」で上書き | | 選択肢ラベル | 接続線をクリックして右パネルで入力 | diff --git a/README.md b/README.md index 1ee4911..6e3b88c 100644 --- a/README.md +++ b/README.md @@ -35,29 +35,29 @@ See [Support](SUPPORT.md) and the [privacy policy](https://htkym.github.io/Plotr - Navigate large graphs with automatic horizontal and vertical scroll bars, canvas panning, mouse-wheel scrolling, and zoom controls. - Combine automatic layered layout with manually pinned nodes. - Track node body text, writing memos, color tags, characters, and character groups. +- Map character relationships in a dedicated canvas, with named relationship lines, editable label colors, and character images. +- Share character group memberships between Plot and the character graph, and show or hide each group background independently. +- Choose whether story cards display their assigned characters, with defaults for each node type and per-node overrides. - Export the graph as PNG or SVG, or export story routes as Markdown or plain text. - Switch the interface between English and Japanese from **Settings > Language**. ![Editing a selected story node](site/images/screenshots/02-node-details-en.png) -## Requirements +## Character graph and groups -- Windows 10 version 1809 or later, or Windows 11 -- .NET 10 SDK for building from source +Open **Character graph** to arrange characters and connect them with relationship lines. New lines start with the label “Relationship name”. Select a line to edit its relationship name and label colors in **Details**. Selected characters are marked with a thick white ring, and relationship lines stay selectable where they overlap group backgrounds. PNG and JPEG character images are saved with the project. -## Build +A character can belong to several groups. Memberships are shared between **Plot** and **Character graph**, so groups assigned in Plot also appear as backgrounds in the character graph. Changing groups or background colors does not move characters. Nearby members of a group share one background, and distant members get separate backgrounds. Overlapping backgrounds and their names are offset like stairs, and the selected group is drawn in front. Selecting a background shows the group name and members in **Details** and opens editing commands next to it. Use **Group visibility**, next to **Groups**, to show or hide backgrounds. -```powershell -dotnet build .\Plotree.slnx -p:Platform=x64 -``` +In Plot, the character manager shows the group list to the right of the character list, and the assignment list can be expanded or collapsed by group. **Details** lists assigned characters one per line with their groups, and you can select the text to copy it. Whether cards show assigned characters is set separately from the title/body display mode, with defaults for each node type and per-node overrides. Characters are hidden on cards by default, and PNG and SVG exports follow the same setting. -Use `winapp run` or the WinUI BuildAndRun workflow to launch a development build rather than starting the executable directly. +## Requirements + +- Windows 10 version 1809 or later, or Windows 11 -After creating an MSIX bundle and its `.cer` file, build the GitHub direct installer with: +## Notes -```powershell -.\scripts\New-DirectInstaller.ps1 -PackageDirectory .\src\Plotree\AppPackages\ -``` +- Projects saved with Plotree 1.0.5 cannot be opened in version 1.0.4 or earlier. ## License diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ca06946..98727b7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,30 @@ +# Plotree v1.0.5 + +2026-10-04 + +## 機能追加 + +- 人物相関図を追加しました。人物同士を線でつなぎ、関係名とラベルの色を編集できます。人物の PNG / JPEG 画像はプロジェクトと一緒に保存されます。 +- 人物の複数グループ所属をツリーと関係図で共有します。背景の表示・非表示、色の変更、グループの詳細表示と近くに開く編集メニューに対応しました。 +- ツリーの人物管理にグループ一覧を並べ、人物の割り当てをグループごとに展開・折りたたみできるようにしました。 +- 詳細の登場人物をグループ名付きで一人ずつ別の行に表示し、文字を選択してコピーできるようにしました。 +- カードに登場人物を表示する設定を追加しました。種類ごとの既定値とノード個別の設定を選べ、SVG 出力にも反映されます。既存のプロジェクトでは初期状態は非表示です。 + +## 修正内容 + +- 選択人物の太い白枠を全周表示し、関係の線の選択範囲を広げました。線はグループ背景より前面で選択できます。 +- 重なるグループ背景と名前を階段状に表示し、選択したグループを手前に出します。背景の範囲は各グループ自身の所属人物から求め、別グループの人物移動で拡大しないようにしました。 +- グループ分けと色の変更で人物の位置を維持し、離れた人物には個別の背景を表示します。 +- 上部メニューを項目の下、アプリ画面内に開くようにしました。 +- 再描画を UI スレッドで集約し、ドラッグ中は保留します。ドラッグ終了時は操作状態を解除してからキャプチャを解放するよう変更しました。 + +## ダウンロード + +- [Microsoft Store](https://apps.microsoft.com/detail/9pfl8jdmmtx6)(推奨)からインストールできます。 +- GitHub から直接インストールする場合は、`Plotree_1.0.5.0_DirectInstaller.zip` をダウンロードしてすべて展開し、`Install-Plotree.bat` を実行してください。初回は自己署名証明書を登録するため、管理者の許可が必要です。 + +直接インストール版には x64 と ARM64 のパッケージが含まれています。自動更新には対応していません。 + # Plotree v1.0.4 2026-08-24 diff --git a/site/content/user-manual-en.md b/site/content/user-manual-en.md index f1ce925..5053da7 100644 --- a/site/content/user-manual-en.md +++ b/site/content/user-manual-en.md @@ -43,6 +43,22 @@ Use **Characters** to create people in the story and **Groups** to organize them ![Managing characters and group membership](../images/screenshots/03-characters-en.png) +In the plot tree, the character manager shows group memberships alongside the character list. Character assignment sections expand and collapse by group. Details list each character on a separate line with all its groups, and text can be selected and copied. Card appearance has an independent character visibility setting, with a document default and per-card override. Existing documents initially keep characters hidden on cards. + +## Build a character graph + +Choose **Character graph** in the left navigation to see every character in the project as a circular icon. Create and manage characters and groups on the **Plot** screen. Select a character to see their name, groups, and relationships on the right. + +Select two characters with Ctrl-click or a marquee to create and save a line immediately. You can also drag the connector beside one character to another. The new relationship starts with the label “Relationship name”. **Add relationship** lets you choose the pair in the editor. Select a line label or a relationship list entry to edit either endpoint, the name, label background, or text color, or to delete the relationship. Colors must be opaque `#RRGGBB` values. Each pair can have only one relationship, regardless of character order. A character cannot be linked to themselves. + +Click a character to select it, and Ctrl-click to select several. Drag empty space to select a rectangle; hold Shift or Ctrl to add the rectangle to the current selection. Drag a selected character to move the selection together. Pan with the middle mouse button or hold Space while left-dragging. + +Use the **Groups** menu to create a group from the selected characters or add them to an existing group. Edit group names and background colors from the same menu. Group assignments also appear in the Plot screen. Characters may belong to several groups. Removing a selection from all groups removes its shared membership in both tabs. Choose **Choose image** on a character to set a PNG or JPEG avatar. The image fills the circular icon from the center and is stored inside the project file. + +Use Ctrl + mouse wheel or the toolbar buttons to zoom, and **Fit all** to bring everyone into view. Character positions, relationships, graph groups, and images are saved in the `.plotree` file. A drag or relationship and group edit can be undone and redone. Use Tab to reach character icons and the relationship list by keyboard. + +Groups use the same memberships in both tabs. Grouping and changing background colors preserve character and relationship positions. Each background is calculated from its own group members: nearby members share a background, and distant members have separate backgrounds. Moving a character who belongs only to another group does not resize that background. Overlapping backgrounds are offset; newer groups appear above older ones, and selecting a background brings it to the front and shows its name and members in Details. Click a background for its nearby group menu. Use **Group visibility** next to **Groups** to show or hide backgrounds without changing membership. New connections start with the label “Relationship name”. Selected characters have a thick white ring. Relationship lines keep their visible widths and have a wider click target above group backgrounds. Every group name is a separate selectable button; overlapping backgrounds and their names move together in a staircase, with the frontmost at the lower left and each background behind it further up and right. Selecting another group preserves character positions. Top menus open below their buttons and stay inside the application window. + ## Arrange the graph Drag a node to move it. Moved nodes become pinned, so automatic layout preserves their positions. Choose **Left to right** or **Top to bottom** from the layout direction list, then select **Auto layout** to arrange unpinned nodes. Use **Relayout all** from the split-button menu to clear pins and arrange every node. diff --git a/site/content/user-manual-ja.md b/site/content/user-manual-ja.md index 1671471..be56344 100644 --- a/site/content/user-manual-ja.md +++ b/site/content/user-manual-ja.md @@ -43,6 +43,22 @@ Plotree は、小説やゲームの分岐する物語をグラフとして設計 ![登場人物と所属グループを管理する画面](../images/screenshots/03-characters-ja.png) +ツリーの人物管理では、人物一覧の右側にグループ所属一覧を表示します。人物割り当てはグループごとに展開・折りたたみできます。詳細には人物ごとに1行ずつ、所属する全グループとともに表示し、文字を選択してコピーできます。カードの外観では、人物の表示・非表示をドキュメントの既定値とカードごとの上書きで設定できます。既存文書では、初期状態のカードは人物を非表示にします。 + +## 人物相関図を作る + +左側のナビゲーションで**人物相関図**を選ぶと、プロジェクト内の人物が円形のアイコンで表示されます。人物とグループの追加・編集は**プロット**画面で行います。人物を選ぶと、右側で名前、所属グループ、関係を確認できます。 + +人物を Ctrl + クリックや範囲選択で2人選ぶと、その場で線を作成して保存します。アイコンの横にある接続ボタンから別の人物へドラッグしても作成できます。作成時のラベルは「関係名」です。**関係を追加**から2人を指定して作成することもできます。関係の線上にあるラベル、または右側の関係一覧を選ぶと、両端の人物、関係名、ラベルの背景色と文字色を編集・削除できます。色は不透明な `#RRGGBB` で指定します。同じ2人の関係は1件までで、人物の順序を入れ替えても重複として扱われます。自分自身との関係は作成できません。 + +人物をクリックすると選択し、Ctrl + クリックで複数の人物を選択できます。空白部分をドラッグすると範囲選択できます。Shift または Ctrl を押しながら範囲選択すると、現在の選択へ追加します。選択した人物を動かすと、まとめて移動します。空白部分のパンには中ボタンドラッグ、または Space キーを押しながら左ドラッグを使います。 + +上部の**グループ分け**メニューから、選択した人物でグループを作成するか、既存のグループへ追加できます。グループ名と背景色はメニューから編集できます。割り当てた人物はプロット画面でもそのグループの所属として表示されます。人物は複数のグループに所属できます。**選択した人物をすべてのグループから外す**操作では、両タブで共有される所属を解除します。人物アイコンの**画像を選択**から PNG または JPEG を設定できます。画像は中央を基準に円の内側いっぱいに表示し、プロジェクトファイルに含めて保存します。 + +人物のアイコンをドラッグすると位置を変更できます。Ctrl + マウスホイールまたは上部のボタンで拡大・縮小し、**全体表示**で人物全員を表示範囲に収められます。人物の位置、関係、グループ、画像は `.plotree` ファイルに保存されます。移動1回や関係・グループの編集は、元に戻す・やり直しの対象です。人物や関係一覧は Tab キーで移動して選択できます。 + +グループ所属はツリーと人物の両タブで共有されます。グループ分けや背景色の変更では人物と線の位置は変わりません。各グループ自身の所属人物だけから背景の範囲を求め、近くの人物は背景をまとめ、離れた人物には個別の背景を表示します。別グループだけに所属する人物の移動では、その背景の大きさは変わりません。重なる背景は少しずらし、後から作成したグループを上に表示します。背景を選択するとそのグループが手前になり、詳細に名前と所属人物が表示されます。背景をクリックすると、そのそばにグループのメニューが開きます。「グループ分け」の右隣にある「グループの表示」で、所属を保ったまま背景を表示・非表示にできます。新しい関係の初期ラベルは「関係名」です。選択した人物には太い白い輪を表示します。関係の線の見た目の太さは保ったままクリック範囲を広げ、グループ背景より前面で選択できます。各グループ名は個別に選択できるボタンです。重なる背景と名前は一緒に階段状にずらし、最前面を左下、その後ろを順に右上に表示します。選択を切り替えても人物の位置は変わりません。上部メニューは項目の下、アプリ画面内に開きます。 + ## グラフを整える ノードをドラッグすると位置を移動できます。移動したノードはピン留めされ、自動整列でも位置が維持されます。レイアウト方向で **左から右** または **上から下** を選び、**自動整列**を実行すると、ピン留めされていないノードが配置されます。分割ボタンの **すべて再整列** は、ピンを解除してすべてのノードを配置します。 diff --git a/src/Plotree/MainPage.xaml b/src/Plotree/MainPage.xaml index 2e189aa..f20d516 100644 --- a/src/Plotree/MainPage.xaml +++ b/src/Plotree/MainPage.xaml @@ -68,7 +68,7 @@ - + - + + + + + + + + + + + + - + @@ -319,7 +334,7 @@ - + @@ -352,7 +367,7 @@ AutomationProperties.Name="Add color tag"> - + - + - + + - - @@ -477,10 +489,11 @@ - + + @@ -494,8 +507,8 @@ - - + + @@ -514,56 +527,66 @@ VerticalAlignment="Bottom" AutomationProperties.AutomationId="AddCharacterButton" /> - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -577,7 +600,7 @@ - + @@ -662,7 +685,7 @@ - + - + @@ -741,9 +764,9 @@ BorderThickness="1,0,0,0"> - + - - - - - @@ -775,11 +798,11 @@ Spacing="8" Visibility="{x:Bind local:MainPage.BoolToVisibility(ViewModel.IsMultiNodeSelected), Mode=OneWay}" AutomationProperties.AutomationId="MultiSelectionPanel"> - - - - + - +