diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e6fb15..44f04da 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,6 +47,16 @@ Those name, publisher, and publisher display strings are `packageName`, `publish The `.msix` is not a GitHub release asset. +Store listing copy: + +| Field | Document | +|-------|----------| +| Short description, description, extra requirements | [packaging/microsoft-store/description.md](packaging/microsoft-store/description.md) | +| Product features (up to 20 bullets, 200 characters each) | [packaging/microsoft-store/product-features.md](packaging/microsoft-store/product-features.md) | +| Keywords (up to 7, 40 characters each, 21 words total) | [packaging/microsoft-store/keywords.md](packaging/microsoft-store/keywords.md) | +| Copyright, additional license terms, Developed by | [packaging/microsoft-store/additional-info.md](packaging/microsoft-store/additional-info.md) | +| Store logos (9:16 poster, 1:1 box art, app tiles) | [packaging/microsoft-store/logos.md](packaging/microsoft-store/logos.md) | + ### System requirements (Properties) Partner Center → **Properties** → **System requirements**. A blank cell stays unset. Minimum is what the Store may warn on; Recommended does not warn. diff --git a/packaging/microsoft-store/New-StoreLogos.ps1 b/packaging/microsoft-store/New-StoreLogos.ps1 new file mode 100644 index 0000000..f0b4a75 --- /dev/null +++ b/packaging/microsoft-store/New-StoreLogos.ps1 @@ -0,0 +1,157 @@ +# Renders Microsoft Store logo PNGs from the Cluster Console icon geometry. +# Requires Windows PowerShell 5.1 (System.Drawing). Output: packaging/microsoft-store/logos/ + +$ErrorActionPreference = "Stop" +Add-Type -AssemblyName System.Drawing + +$outDir = Join-Path $PSScriptRoot "logos" +New-Item -ItemType Directory -Force -Path $outDir | Out-Null + +Add-Type -ReferencedAssemblies System.Drawing -TypeDefinition @" +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using System.IO; + +public static class StoreLogos { + static readonly Color Bg0 = Color.FromArgb(0x33, 0xA5, 0xCF); + static readonly Color Bg1 = Color.FromArgb(0x00, 0x2A, 0x6A); + static readonly Color Tile0 = Color.White; + static readonly Color Tile1 = Color.FromArgb(0xC5, 0xE8, 0xF6); + static readonly Color Ink = Color.FromArgb(0x00, 0x2A, 0x6A); + + public static void SaveAll(string dir) { + SaveIcon(Path.Combine(dir, "box-1x1-2160x2160.png"), 2160); + SaveIcon(Path.Combine(dir, "box-1x1-1080x1080.png"), 1080); + SaveIcon(Path.Combine(dir, "tile-1x1-300x300.png"), 300); + SaveIcon(Path.Combine(dir, "tile-1x1-150x150.png"), 150); + SaveIcon(Path.Combine(dir, "tile-1x1-71x71.png"), 71); + SavePoster(Path.Combine(dir, "poster-9x16-1440x2160.png"), 1440, 2160); + SavePoster(Path.Combine(dir, "poster-9x16-720x1080.png"), 720, 1080); + } + + static void SaveIcon(string path, int size) { + using (var bmp = new Bitmap(size, size, PixelFormat.Format32bppArgb)) + using (var g = Graphics.FromImage(bmp)) { + Quality(g); + DrawBackground(g, size, size); + DrawMark(g, 0f, 0f, size / 256f, true); + bmp.Save(path, ImageFormat.Png); + } + } + + static void SavePoster(string path, int w, int h) { + using (var bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb)) + using (var g = Graphics.FromImage(bmp)) { + Quality(g); + DrawBackground(g, w, h); + float scale = 0.62f * w / 184f; + float markH = 148f * scale; + using (var font = FitFont(g, "Cluster Console", w * 0.84f, w * 0.072f)) { + SizeF text = g.MeasureString("Cluster Console", font); + float gap = h * 0.045f; + float block = markH + gap + text.Height; + float top = (h - block) / 2f; + float ox = (w / 2f) - (128f * scale); + float oy = (top + markH / 2f) - (124f * scale); + DrawMark(g, ox, oy, scale, false); + using (var brush = new SolidBrush(Color.White)) + using (var fmt = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Near }) { + g.DrawString("Cluster Console", font, brush, new RectangleF(0f, top + markH + gap, w, text.Height + 4f), fmt); + } + } + bmp.Save(path, ImageFormat.Png); + } + } + + static void DrawBackground(Graphics g, int w, int h) { + using (var brush = new LinearGradientBrush( + new PointF(w * 0.2f, 0f), + new PointF(w * 0.9f, h), + Bg0, + Bg1)) { + g.FillRectangle(brush, 0, 0, w, h); + } + } + + static void DrawMark(Graphics g, float ox, float oy, float s, bool shine) { + if (shine) { + using (var brush = new SolidBrush(Color.FromArgb(41, 255, 255, 255))) { + float rx = 150f * s; + float ry = 88f * s; + g.FillEllipse(brush, ox + (128f * s) - rx, oy + (8f * s) - ry, rx * 2f, ry * 2f); + } + } + Tile(g, ox, oy, s, 36f, 50f); + Tile(g, ox, oy, s, 132f, 50f); + Tile(g, ox, oy, s, 84f, 110f); + using (var pen = new Pen(Ink, 14f * s)) { + pen.StartCap = LineCap.Round; + pen.EndCap = LineCap.Round; + pen.LineJoin = LineJoin.Round; + g.DrawLines(pen, new[] { + new PointF(ox + 112f * s, oy + 134f * s), + new PointF(ox + 152f * s, oy + 154f * s), + new PointF(ox + 112f * s, oy + 174f * s) + }); + } + } + + static void Tile(Graphics g, float ox, float oy, float s, float x, float y) { + float rx = ox + x * s; + float ry = oy + y * s; + float side = 88f * s; + float radius = 20f * s; + using (var path = RoundedRect(rx, ry, side, side, radius)) + using (var brush = new LinearGradientBrush( + new PointF(rx, ry), + new PointF(rx + side, ry + side), + Tile0, + Tile1)) { + g.FillPath(brush, path); + } + } + + static GraphicsPath RoundedRect(float x, float y, float w, float h, float r) { + float d = r * 2f; + var path = new GraphicsPath(); + path.AddArc(x, y, d, d, 180f, 90f); + path.AddArc(x + w - d, y, d, d, 270f, 90f); + path.AddArc(x + w - d, y + h - d, d, d, 0f, 90f); + path.AddArc(x, y + h - d, d, d, 90f, 90f); + path.CloseFigure(); + return path; + } + + static Font FitFont(Graphics g, string text, float maxWidth, float startPx) { + float size = startPx; + Font font = null; + while (size > 12f) { + if (font != null) font.Dispose(); + font = new Font("Segoe UI", size, FontStyle.Bold, GraphicsUnit.Pixel); + if (g.MeasureString(text, font).Width <= maxWidth) return font; + size -= 1f; + } + return font; + } + + static void Quality(Graphics g) { + g.SmoothingMode = SmoothingMode.AntiAlias; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; + } +} +"@ + +[StoreLogos]::SaveAll($outDir) + +Add-Type -AssemblyName System.Drawing +Get-ChildItem $outDir -Filter *.png | ForEach-Object { + $img = [System.Drawing.Image]::FromFile($_.FullName) + "{0}`t{1}x{2}`t{3:N0} KB" -f $_.Name, $img.Width, $img.Height, ($_.Length / 1KB) + $img.Dispose() +} diff --git a/packaging/microsoft-store/additional-info.md b/packaging/microsoft-store/additional-info.md new file mode 100644 index 0000000..f72eb17 --- /dev/null +++ b/packaging/microsoft-store/additional-info.md @@ -0,0 +1,29 @@ +# Microsoft Store additional info + +Paste into Partner Center → Store listings. Field limits are from [Add additional information](https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/msix/add-additional-information). + +## Copyright and trademark info + +200 characters. + +```text +© Maksym Sadovnychyy (MAKS-IT). Apache License 2.0. +``` + +## Additional license terms + +Leave this blank only when customers should be licensed solely under the Standard Application License Terms. This product is Apache 2.0 ([LICENSE.md](../../LICENSE.md)), so paste the license URL. A single URL is shown to customers as a link. Plain text is limited to 10,000 characters. + +```text +http://www.apache.org/licenses/LICENSE-2.0 +``` + +## Developed by + +255 characters. This is separate from **Published by**, which stays the publisher display name on the account. + +```text +Maksym Sadovnychyy (MAKS-IT) +``` + +The listing description is in [description.md](description.md). Product features are in [product-features.md](product-features.md). Keywords are in [keywords.md](keywords.md). diff --git a/packaging/microsoft-store/description.md b/packaging/microsoft-store/description.md new file mode 100644 index 0000000..a87ac32 --- /dev/null +++ b/packaging/microsoft-store/description.md @@ -0,0 +1,60 @@ +# Microsoft Store description + +Paste into Partner Center → Store listings. Plain text only. URLs in these fields are not clickable; put the privacy policy, support site, and license in their own submission fields. + +Leave **What's new in this version** empty on the first submission. Use it only for a later update (1,500 characters). + +Hardware checkboxes (keyboard, memory, processor) stay in [CONTRIBUTING.md](../../CONTRIBUTING.md) under **System requirements (Properties)**. + +## Short description + +Shown at the top of the listing. The field allows 1,000 characters. Keep this under 270 so every Store view shows the full sentence. + +```text +A desktop app for an existing Kubernetes cluster. Connect with your kubeconfig, then browse resources, apply YAML, follow pod logs, open a shell, and forward ports to this PC. +``` + +## Description + +Required. Up to 10,000 characters. This text is different from the short description so the page does not repeat the same paragraph. + +```text +Work on a Kubernetes cluster from Windows without keeping a browser dashboard or a terminal open for every task. + +Connect with the kubeconfig you already use. What you can see and change is exactly what that identity is allowed to do. Choosing a context in the catalog also sets kubectl's current context, so this app and the command line stay on the same cluster. + +Move through the cluster from one navigator: cluster and nodes, applications, workloads, config, network, storage, namespaces, events, Helm releases, Dapr, access control, and custom resources. Tables keep filters and sort for each cluster. Open a row for YAML, events, and status. + +Everyday changes stay in the same window. Apply or create YAML, scale and restart workloads, trigger a CronJob, and cordon or drain a node. On a pod, follow the log or open a shell. Port forwards are saved, restored when you reconnect, and a double-click on an active forward opens it in the browser. + +CPU and memory appear on the overview, applications, and workload tables when the cluster has metrics-server. Persistent volumes and claims open a file browser for viewing, editing, downloading, and uploading files. Helm and Dapr pages list objects that are already in the cluster. + +Chat is optional and stays on this PC. It talks to a local Ollama server and can read issues, YAML, logs, and events for the resource you selected. Apply, restart, and delete stay manual. Cluster data is not sent to a cloud AI service. + +Your layout, open clusters, and saved port forwards are stored on this computer. Cluster Console talks to a cluster you already run. + +You need 64-bit Windows, a kubeconfig that can reach a running cluster, and a network path to that cluster's API. Install Ollama locally only if you want Chat. +``` + +Product features (up to 20 bullets) are in [product-features.md](product-features.md). Keywords are in [keywords.md](keywords.md). Copyright, additional license terms, and Developed by are in [additional-info.md](additional-info.md). + +## Additional system requirements + +Optional text lines, separate from the Properties hardware table. Up to 11 minimum and 11 recommended. Each line is at most 200 characters. + +Minimum: + +```text +64-bit Windows 10 or Windows 11 +A kubeconfig with permission to reach a running Kubernetes cluster +Network access to that cluster's API server +``` + +Recommended: + +```text +metrics-server installed in the cluster, for CPU and memory columns +Ollama on this PC, only if you use Chat +``` + +Category: Developer tools. diff --git a/packaging/microsoft-store/keywords.md b/packaging/microsoft-store/keywords.md new file mode 100644 index 0000000..ca4bd60 --- /dev/null +++ b/packaging/microsoft-store/keywords.md @@ -0,0 +1,17 @@ +# Microsoft Store keywords + +Paste into Partner Center → Store listings → **Keywords**. One keyword per line. Hidden from customers. + +Up to 7 keywords. 40 character limit for each keyword. No more than 21 separate words across all keywords. + +```text +Kubernetes GUI +kubeconfig +pod logs +YAML +Helm releases +port forwarding +Dapr +``` + +The listing description is in [description.md](description.md). Product features are in [product-features.md](product-features.md). diff --git a/packaging/microsoft-store/logos.md b/packaging/microsoft-store/logos.md new file mode 100644 index 0000000..c3ecd6e --- /dev/null +++ b/packaging/microsoft-store/logos.md @@ -0,0 +1,37 @@ +# Microsoft Store logos + +Upload the PNG files in [logos/](logos/) on Partner Center → Store listings → **Store logos**. They use the same mark as `src/MaksIT.ClusterConsole.UI/Assets/icon.svg`. + +Regenerate with `packaging/microsoft-store/New-StoreLogos.ps1` (Windows PowerShell 5.1). + +PNG only. Poster and box art must be under 50 MB. App tiles must be under 5 MB. + +## 9:16 Poster art + +Main logo for Windows 10 and Windows 11. Required for Xbox. + +| File | Size | +|------|------| +| [logos/poster-9x16-720x1080.png](logos/poster-9x16-720x1080.png) | 720 × 1080 | +| [logos/poster-9x16-1440x2160.png](logos/poster-9x16-1440x2160.png) | 1440 × 2160 | + +## 1:1 Box art + +Used in Store layouts. Used as the main logo when the 9:16 poster is missing. + +| File | Size | +|------|------| +| [logos/box-1x1-1080x1080.png](logos/box-1x1-1080x1080.png) | 1080 × 1080 | +| [logos/box-1x1-2160x2160.png](logos/box-1x1-2160x2160.png) | 2160 × 2160 | + +## Store display images + +1:1 app tile icon, shown in the Store for Windows 10 and Windows 11. These replace the logo images from the package when uploaded. + +| File | Size | +|------|------| +| [logos/tile-1x1-300x300.png](logos/tile-1x1-300x300.png) | 300 × 300 | +| [logos/tile-1x1-150x150.png](logos/tile-1x1-150x150.png) | 150 × 150 | +| [logos/tile-1x1-71x71.png](logos/tile-1x1-71x71.png) | 71 × 71 | + +Listing text is in [description.md](description.md), [product-features.md](product-features.md), [keywords.md](keywords.md), and [additional-info.md](additional-info.md). diff --git a/packaging/microsoft-store/logos/box-1x1-1080x1080.png b/packaging/microsoft-store/logos/box-1x1-1080x1080.png new file mode 100644 index 0000000..d7348c5 Binary files /dev/null and b/packaging/microsoft-store/logos/box-1x1-1080x1080.png differ diff --git a/packaging/microsoft-store/logos/box-1x1-2160x2160.png b/packaging/microsoft-store/logos/box-1x1-2160x2160.png new file mode 100644 index 0000000..6d3f9ca Binary files /dev/null and b/packaging/microsoft-store/logos/box-1x1-2160x2160.png differ diff --git a/packaging/microsoft-store/logos/poster-9x16-1440x2160.png b/packaging/microsoft-store/logos/poster-9x16-1440x2160.png new file mode 100644 index 0000000..e1c5d6e Binary files /dev/null and b/packaging/microsoft-store/logos/poster-9x16-1440x2160.png differ diff --git a/packaging/microsoft-store/logos/poster-9x16-720x1080.png b/packaging/microsoft-store/logos/poster-9x16-720x1080.png new file mode 100644 index 0000000..b440843 Binary files /dev/null and b/packaging/microsoft-store/logos/poster-9x16-720x1080.png differ diff --git a/packaging/microsoft-store/logos/tile-1x1-150x150.png b/packaging/microsoft-store/logos/tile-1x1-150x150.png new file mode 100644 index 0000000..b288992 Binary files /dev/null and b/packaging/microsoft-store/logos/tile-1x1-150x150.png differ diff --git a/packaging/microsoft-store/logos/tile-1x1-300x300.png b/packaging/microsoft-store/logos/tile-1x1-300x300.png new file mode 100644 index 0000000..aaa614f Binary files /dev/null and b/packaging/microsoft-store/logos/tile-1x1-300x300.png differ diff --git a/packaging/microsoft-store/logos/tile-1x1-71x71.png b/packaging/microsoft-store/logos/tile-1x1-71x71.png new file mode 100644 index 0000000..ac51c07 Binary files /dev/null and b/packaging/microsoft-store/logos/tile-1x1-71x71.png differ diff --git a/packaging/microsoft-store/product-features.md b/packaging/microsoft-store/product-features.md new file mode 100644 index 0000000..40850c1 --- /dev/null +++ b/packaging/microsoft-store/product-features.md @@ -0,0 +1,25 @@ +# Microsoft Store product features + +Paste into Partner Center → Store listings → **Product features**. + +Short summaries of the product's key features. Displayed as a bulleted list. Up to 20 app features. Each feature is at most 200 characters. + +```text +Connect with the kubeconfig you already use. Access follows that identity's permissions. +Choose a context in the catalog to set kubectl's current context. +Browse cluster, nodes, workloads, config, network, storage, namespaces, events, access control, and custom resources. +View, apply, create, and delete resource YAML, including force delete. +Scale and restart workloads, and trigger a CronJob. +Cordon and drain nodes. +Follow pod logs and open a shell in a container. +Save port forwards, restore them on reconnect, and open an active forward in the browser. +Show CPU and memory when the cluster has metrics-server. +Group applications by instance and namespace from standard labels. +Browse, edit, download, and upload files on a persistent volume or claim. +List Helm releases that are already installed in the cluster. +List Dapr components, configurations, subscriptions, resiliency, endpoints, sidecars, and the control plane. +Ask a local Ollama model about the selected resource. Chat can read only. +Keep filters, sort, layout, and port forwards on this PC. +``` + +The listing description and the other Store text fields are in [description.md](description.md). diff --git a/utils/engines/release/scriptSettings.json b/utils/engines/release/scriptSettings.json index 73d1001..a12d1f6 100644 --- a/utils/engines/release/scriptSettings.json +++ b/utils/engines/release/scriptSettings.json @@ -71,7 +71,7 @@ "packageName": "MAKS-IT.ClusterConsole", "publisher": "CN=FCC8C0E7-6D5F-4028-B8EE-903B88C0C8F9", "publisherDisplayName": "MaksIT", - "displayName": "MaksIT Cluster Console", + "displayName": "Cluster Console", "executableName": "MaksIT.ClusterConsole.UI.exe", "runtimeIdentifier": "win-x64", "iconPath": "..\\..\\..\\src\\MaksIT.ClusterConsole.UI\\Assets\\icon.png",