diff --git a/MarketExtension/Helpers/DataSourceAttribution.cs b/MarketExtension/Helpers/DataSourceAttribution.cs new file mode 100644 index 0000000..e81fe16 --- /dev/null +++ b/MarketExtension/Helpers/DataSourceAttribution.cs @@ -0,0 +1,60 @@ +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; +using MarketExtension.Properties; + +namespace MarketExtension; + +// The active live data provider's attribution, shown wherever its data is displayed. Twelve Data's terms +// REQUIRE attribution ("Data provided by Twelve Data", with a link) on any surface that shows their data; +// Finnhub gets the same treatment as good practice / store-reviewer comfort (its terms don't mandate it). +// Reflects the ACTIVE source so it's never wrong — and never credits a provider whose data isn't on screen: +// * Demo mode -> null (no live data is shown; the demo status row already says "sample data") +// * Twelve Data key set -> Twelve Data (TD serves every category, so its must-do is always covered) +// * else Finnhub key set -> Finnhub +// * else -> null (nothing is being priced; the missing-key hint explains the blanks) +// +// The row is clickable and opens the provider's site — the desktop-app equivalent of Twelve Data's required +// "dofollow link to twelvedata.com" (a CmdPal extension has no , so a launch command is the faithful +// analog). Parallels ApiKeyHint / RateLimitHint: a static helper returning a row (or text) or null. The +// keyless FX source (Frankfurter / ECB) and the logo source (Elbstream) are credited separately — ECB on +// the Data Sources page, Elbstream via AssetIconResolver.AttributionRow(). +internal static class DataSourceAttribution +{ + private const string LinkGlyph = ""; // Segoe MDL2 Link + + // The attribution row for a list page, or null when no live provider is serving (demo / no key). + public static IListItem? Row() + { + var (name, url) = Active(); + if (name is null) + return null; + + return new ListItem(new OpenUrlCommand(url!, Strings.Format(Resources.DataAttribution_OpenName, name))) + { + Title = Strings.Format(Resources.DataAttribution_Title, name), + Subtitle = Resources.DataAttribution_Subtitle, + Icon = new IconInfo(LinkGlyph), + }; + } + + // The same credit as plain text (for the symbol-detail adaptive card), or null when none applies. + public static string? Text() + { + var (name, _) = Active(); + return name is null ? null : Strings.Format(Resources.DataAttribution_Title, name); + } + + // (display name, site URL) of the active live data source, or (null, null) when none is serving. + // Brand names are intentionally NOT localized. + private static (string? Name, string? Url) Active() + { + var settings = MarketSettingsManager.Instance; + if (settings.DemoMode) + return (null, null); + if (settings.HasTwelveDataApiKey) + return ("Twelve Data", "https://twelvedata.com"); + if (settings.HasFinnhubApiKey) + return ("Finnhub", "https://finnhub.io"); + return (null, null); + } +} diff --git a/MarketExtension/MarketExtension.csproj b/MarketExtension/MarketExtension.csproj index 70f8ce2..d70416a 100644 --- a/MarketExtension/MarketExtension.csproj +++ b/MarketExtension/MarketExtension.csproj @@ -12,7 +12,7 @@ CostaFotiadis.MarketsExtensionforCommandPalette CN=3D57AA92-97A9-42D2-8CB0-4207D9145514 - 1.0.0.1 + 1.1.0.0 true enable diff --git a/MarketExtension/Package.appxmanifest b/MarketExtension/Package.appxmanifest index 2d0be9b..a930fce 100644 --- a/MarketExtension/Package.appxmanifest +++ b/MarketExtension/Package.appxmanifest @@ -11,7 +11,7 @@ + Version="1.1.0.0" /> Markets Extension for Command Palette diff --git a/MarketExtension/Pages/DataSourcesPage.cs b/MarketExtension/Pages/DataSourcesPage.cs index 889e906..43a8937 100644 --- a/MarketExtension/Pages/DataSourcesPage.cs +++ b/MarketExtension/Pages/DataSourcesPage.cs @@ -30,6 +30,21 @@ Settings and the plan attached to each. Some data, such as currency reference rates, can come from a public source that needs no key. + ## Attribution + + Where data is shown, it is credited to its source: + + - Stocks, crypto, and currencies — [Twelve Data](https://twelvedata.com) + or [Finnhub](https://finnhub.io), depending on the key you've added. + - Currency reference rates — the European Central Bank, via + [Frankfurter](https://www.frankfurter.dev). + - Company and asset logos — [Elbstream](https://elbstream.com). + + These providers are independent. This app is not affiliated with, + endorsed by, or sponsored by them, and their names and logos belong to + their respective owners. You are responsible for using your keys within + each provider's terms. + ## Your keys, your data The keys are yours. You create them with the provider; this app keeps diff --git a/MarketExtension/Pages/PricedListPage.cs b/MarketExtension/Pages/PricedListPage.cs index 131662d..a4cc2ee 100644 --- a/MarketExtension/Pages/PricedListPage.cs +++ b/MarketExtension/Pages/PricedListPage.cs @@ -167,6 +167,8 @@ public override IListItem[] GetItems() rows.AddRange(cached.Where(Matches).Select(BuildRow)); rows.Add(new ListItem(new RefreshCommand(this)) { Title = Resources.Action_Refresh + " 🔄" }); rows.Add(AssetIconResolver.AttributionRow()); // Elbstream logo credit (rows above show logos) + if (DataSourceAttribution.Row() is { } dataCredit) // Twelve Data (required) / Finnhub data credit + rows.Add(dataCredit); if (ApiKeyHint.StatusRow() is { } hint) // no key → explain blanks; demo mode → flag sample data rows.Add(hint); if (RateLimitHint.Row() is { } banner) // throttled → surface at the TOP so it's seen without scrolling diff --git a/MarketExtension/Pages/SearchPage.cs b/MarketExtension/Pages/SearchPage.cs index 20b57e4..34cbfd6 100644 --- a/MarketExtension/Pages/SearchPage.cs +++ b/MarketExtension/Pages/SearchPage.cs @@ -147,7 +147,11 @@ private IListItem[] SearchItems() items.Add(BuildResultItem(instrument)); if (_searchResults.Count > 0) + { items.Add(AssetIconResolver.AttributionRow()); // Elbstream logo credit (results show logos) + if (DataSourceAttribution.Row() is { } dataCredit) // results come from the live provider's data + items.Add(dataCredit); + } } if (ApiKeyHint.StatusRow() is { } hint) // no key → explain empty search; demo mode → flag sample data diff --git a/MarketExtension/Pages/SymbolDetailPage.cs b/MarketExtension/Pages/SymbolDetailPage.cs index 781ab75..bbed193 100644 --- a/MarketExtension/Pages/SymbolDetailPage.cs +++ b/MarketExtension/Pages/SymbolDetailPage.cs @@ -298,6 +298,7 @@ private string BuildData(DomainCandleSeries? series, ChartRange range) data["showStatus"] = true; data["statusText"] = Strings.Format(Resources.Chart_Loading, range.Label()); data["attributionText"] = Resources.Attribution_Title; + SetDataAttribution(data); return data.ToJsonString(); } @@ -313,9 +314,21 @@ private string BuildData(DomainCandleSeries? series, ChartRange range) ? string.Empty : Resources.Chart_NoData; data["attributionText"] = Resources.Attribution_Title; + SetDataAttribution(data); return data.ToJsonString(); } + // The active live data provider's credit (Twelve Data required / Finnhub good-practice), or hidden + // when none applies (demo mode / no key). Text-only here: the chart is a single card with no + // clickable rows, and every list/search surface that shows the same data carries the clickable + // version (DataSourceAttribution.Row()). + private static void SetDataAttribution(JsonObject data) + { + var credit = DataSourceAttribution.Text(); + data["dataAttributionText"] = credit ?? string.Empty; + data["hasDataAttribution"] = credit is not null; + } + // Static card structure; values bind from DataJson. The five Action.Submit buttons each carry // their range label, which SubmitForm reads back. private const string Template = """ @@ -341,7 +354,8 @@ private string BuildData(DomainCandleSeries? series, ChartRange range) { "type": "Action.Submit", "title": "5Y", "data": { "range": "5Y" } } ] }, - { "type": "TextBlock", "text": "${attributionText}", "isSubtle": true, "wrap": true, "spacing": "Medium" } + { "type": "TextBlock", "text": "${attributionText}", "isSubtle": true, "wrap": true, "spacing": "Medium" }, + { "type": "TextBlock", "text": "${dataAttributionText}", "isSubtle": true, "wrap": true, "spacing": "Small", "$when": "${hasDataAttribution}" } ] } """; diff --git a/MarketExtension/Properties/Resources.Designer.cs b/MarketExtension/Properties/Resources.Designer.cs index acf942d..2cb03b8 100644 --- a/MarketExtension/Properties/Resources.Designer.cs +++ b/MarketExtension/Properties/Resources.Designer.cs @@ -384,6 +384,33 @@ public static string Currency_ZAR { } } + /// + /// Looks up a localized string similar to Open {0}. + /// + public static string DataAttribution_OpenName { + get { + return ResourceManager.GetString("DataAttribution_OpenName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quotes may be delayed or inaccurate. + /// + public static string DataAttribution_Subtitle { + get { + return ResourceManager.GetString("DataAttribution_Subtitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data provided by {0}. + /// + public static string DataAttribution_Title { + get { + return ResourceManager.GetString("DataAttribution_Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Markets Extension for Command Palette. /// diff --git a/MarketExtension/Properties/Resources.de.resx b/MarketExtension/Properties/Resources.de.resx index 10becf8..26ce202 100644 --- a/MarketExtension/Properties/Resources.de.resx +++ b/MarketExtension/Properties/Resources.de.resx @@ -242,6 +242,15 @@ Elbstream öffnen + + Daten bereitgestellt von {0} + + + Kurse können verzögert oder ungenau sein + + + {0} öffnen + Zur Beobachtungsliste hinzufügen diff --git a/MarketExtension/Properties/Resources.es.resx b/MarketExtension/Properties/Resources.es.resx index c2e2f4d..8d759e2 100644 --- a/MarketExtension/Properties/Resources.es.resx +++ b/MarketExtension/Properties/Resources.es.resx @@ -498,4 +498,13 @@ Won surcoreano (KRW) + + Datos proporcionados por {0} + + + Las cotizaciones pueden estar retrasadas o ser inexactas + + + Abrir {0} + \ No newline at end of file diff --git a/MarketExtension/Properties/Resources.hi.resx b/MarketExtension/Properties/Resources.hi.resx index b984628..856b1ad 100644 --- a/MarketExtension/Properties/Resources.hi.resx +++ b/MarketExtension/Properties/Resources.hi.resx @@ -242,6 +242,15 @@ Elbstream खोलें + + डेटा {0} द्वारा प्रदान किया गया + + + कीमतें विलंबित या अशुद्ध हो सकती हैं + + + {0} खोलें + वॉचलिस्ट में जोड़ें diff --git a/MarketExtension/Properties/Resources.ja.resx b/MarketExtension/Properties/Resources.ja.resx index e0f843a..4ff89cb 100644 --- a/MarketExtension/Properties/Resources.ja.resx +++ b/MarketExtension/Properties/Resources.ja.resx @@ -244,6 +244,15 @@ Elbstream を開く + + データ提供:{0} + + + 価格は遅延または不正確な場合があります + + + {0} を開く + ウォッチリストに追加 diff --git a/MarketExtension/Properties/Resources.ko.resx b/MarketExtension/Properties/Resources.ko.resx index 52e2b43..212d6ac 100644 --- a/MarketExtension/Properties/Resources.ko.resx +++ b/MarketExtension/Properties/Resources.ko.resx @@ -243,6 +243,15 @@ Elbstream 열기 + + 데이터 제공: {0} + + + 시세가 지연되거나 부정확할 수 있습니다 + + + {0} 열기 + 관심목록에 추가 diff --git a/MarketExtension/Properties/Resources.pt-BR.resx b/MarketExtension/Properties/Resources.pt-BR.resx index 5425aec..ad3d1a9 100644 --- a/MarketExtension/Properties/Resources.pt-BR.resx +++ b/MarketExtension/Properties/Resources.pt-BR.resx @@ -242,6 +242,15 @@ Abrir a Elbstream + + Dados fornecidos por {0} + + + As cotações podem estar atrasadas ou imprecisas + + + Abrir {0} + Adicionar à lista de acompanhamento diff --git a/MarketExtension/Properties/Resources.resx b/MarketExtension/Properties/Resources.resx index 6db82ab..dd02f53 100644 --- a/MarketExtension/Properties/Resources.resx +++ b/MarketExtension/Properties/Resources.resx @@ -297,6 +297,15 @@ Open Elbstream + + Data provided by {0} + + + Quotes may be delayed or inaccurate + + + Open {0} + Add to Watchlist diff --git a/MarketExtension/Properties/Resources.zh-Hans.resx b/MarketExtension/Properties/Resources.zh-Hans.resx index 81af3a1..682ff00 100644 --- a/MarketExtension/Properties/Resources.zh-Hans.resx +++ b/MarketExtension/Properties/Resources.zh-Hans.resx @@ -297,6 +297,15 @@ 打开 Elbstream + + 数据由 {0} 提供 + + + 报价可能延迟或不准确 + + + 打开 {0} + 添加到自选 diff --git a/MarketExtension/app.manifest b/MarketExtension/app.manifest index 3761ad7..8de7876 100644 --- a/MarketExtension/app.manifest +++ b/MarketExtension/app.manifest @@ -1,6 +1,6 @@ - + diff --git a/MarketExtension/build-exe.ps1 b/MarketExtension/build-exe.ps1 index 64f897f..2638072 100644 --- a/MarketExtension/build-exe.ps1 +++ b/MarketExtension/build-exe.ps1 @@ -1,7 +1,7 @@ param( [string]$ExtensionName = "MarketExtension", [string]$Configuration = "Release", - [string]$Version = "1.0.0.1", + [string]$Version = "1.1.0.0", [string[]]$Platforms = @("x64", "arm64") ) diff --git a/MarketExtension/setup-template.iss b/MarketExtension/setup-template.iss index ab0388f..507e246 100644 --- a/MarketExtension/setup-template.iss +++ b/MarketExtension/setup-template.iss @@ -1,6 +1,6 @@ ; Inno Setup Script for Markets Extension for Command Palette -#define AppVersion "1.0.0.1" +#define AppVersion "1.1.0.0" [Setup] AppId={{6b38c9aa-bbee-45e9-81e9-cf25707910e7}} diff --git a/notes/store-listing.md b/notes/store-listing.md new file mode 100644 index 0000000..c8fa7d6 --- /dev/null +++ b/notes/store-listing.md @@ -0,0 +1,37 @@ +# Microsoft Store listing copy + +Compliance-reviewed copy for the Partner Center submission. Deliberately avoids: +no "real-time"/"live" guarantees (data can be delayed), no "free" anywhere, no implied +affiliation with Microsoft/providers/exchanges, no trading-advice language; required +disclaimers included. + +## Short description (search-results summary) + +Stocks, crypto, and currencies inside PowerToys Command Palette — watchlist, favorites, and portfolio, using your own market-data key. + +## Description (main listing field) + +**Markets Extension for Command Palette** + +Track stocks, cryptocurrencies, and currencies right inside Microsoft PowerToys Command Palette — no separate app, no browser tab. + +• Search symbols and view quotes and price charts +• Build a watchlist and mark favorites +• Track a portfolio you enter yourself, with multi-currency totals and daily change +• Pin favorites and your portfolio to the Command Palette dock +• Demo mode lets you explore with sample data, fully offline +• No accounts, no telemetry — it runs entirely on your machine + +Connect the app using your own market-data API key from Twelve Data or Finnhub; currency reference rates need no key. Your keys and data stay on your device — the app has no servers and sends each key only to the provider it belongs to. + +**Independent project.** Markets Extension is an open-source, independent extension. It is not affiliated with, endorsed by, or sponsored by Microsoft, Finnhub, Twelve Data, or any exchange or data provider. Company names, ticker symbols, and logos are the property of their respective owners. + +**Disclaimer.** Market data is supplied by third parties and may be delayed or inaccurate. This app is provided for informational purposes only and is not financial, investment, tax, or trading advice, and is not a recommendation to buy or sell any asset. You are solely responsible for your own decisions; always do your own research. + +## Compliance notes + +- **No "real-time"/"live"** — sources can lag (ECB is daily; provider tiers delay), so a real-time claim would risk a misleading-functionality flag. Use "quotes and price charts" + the delay disclaimer. +- **Third-party account disclosure** — Store policy requires disclosing that a third-party service/key is needed; the "Connect the app using your own… key" line covers it. +- **Trademarks** — nominative use only ("for Microsoft PowerToys Command Palette"), plus an explicit non-affiliation + owners'-rights line. +- **Not financial advice** — explicit, since this is a markets app. +- **Never say the API keys are "free"** — provider tiers can change; keyless sources are described as "needs no key".