(build): add Microsoft Store listing copy and logos; shorten the package display name
@ -47,6 +47,16 @@ Those name, publisher, and publisher display strings are `packageName`, `publish
|
|||||||
|
|
||||||
The `.msix` is not a GitHub release asset.
|
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)
|
### 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.
|
Partner Center → **Properties** → **System requirements**. A blank cell stays unset. Minimum is what the Store may warn on; Recommended does not warn.
|
||||||
|
|||||||
157
packaging/microsoft-store/New-StoreLogos.ps1
Normal file
@ -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()
|
||||||
|
}
|
||||||
29
packaging/microsoft-store/additional-info.md
Normal file
@ -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).
|
||||||
60
packaging/microsoft-store/description.md
Normal file
@ -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.
|
||||||
17
packaging/microsoft-store/keywords.md
Normal file
@ -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).
|
||||||
37
packaging/microsoft-store/logos.md
Normal file
@ -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).
|
||||||
BIN
packaging/microsoft-store/logos/box-1x1-1080x1080.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
packaging/microsoft-store/logos/box-1x1-2160x2160.png
Normal file
|
After Width: | Height: | Size: 594 KiB |
BIN
packaging/microsoft-store/logos/poster-9x16-1440x2160.png
Normal file
|
After Width: | Height: | Size: 325 KiB |
BIN
packaging/microsoft-store/logos/poster-9x16-720x1080.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
packaging/microsoft-store/logos/tile-1x1-150x150.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
packaging/microsoft-store/logos/tile-1x1-300x300.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
packaging/microsoft-store/logos/tile-1x1-71x71.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
25
packaging/microsoft-store/product-features.md
Normal file
@ -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).
|
||||||
@ -71,7 +71,7 @@
|
|||||||
"packageName": "MAKS-IT.ClusterConsole",
|
"packageName": "MAKS-IT.ClusterConsole",
|
||||||
"publisher": "CN=FCC8C0E7-6D5F-4028-B8EE-903B88C0C8F9",
|
"publisher": "CN=FCC8C0E7-6D5F-4028-B8EE-903B88C0C8F9",
|
||||||
"publisherDisplayName": "MaksIT",
|
"publisherDisplayName": "MaksIT",
|
||||||
"displayName": "MaksIT Cluster Console",
|
"displayName": "Cluster Console",
|
||||||
"executableName": "MaksIT.ClusterConsole.UI.exe",
|
"executableName": "MaksIT.ClusterConsole.UI.exe",
|
||||||
"runtimeIdentifier": "win-x64",
|
"runtimeIdentifier": "win-x64",
|
||||||
"iconPath": "..\\..\\..\\src\\MaksIT.ClusterConsole.UI\\Assets\\icon.png",
|
"iconPath": "..\\..\\..\\src\\MaksIT.ClusterConsole.UI\\Assets\\icon.png",
|
||||||
|
|||||||