Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions MarketExtension/Helpers/DataSourceAttribution.cs
Original file line number Diff line number Diff line change
@@ -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 <a rel>, 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);
}
}
2 changes: 1 addition & 1 deletion MarketExtension/MarketExtension.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<AppxPackageIdentityName>CostaFotiadis.MarketsExtensionforCommandPalette</AppxPackageIdentityName>
<AppxPackagePublisher>CN=3D57AA92-97A9-42D2-8CB0-4207D9145514</AppxPackagePublisher>
<AppxPackageVersion>1.0.0.1</AppxPackageVersion>
<AppxPackageVersion>1.1.0.0</AppxPackageVersion>
<EnableMsixTooling>true</EnableMsixTooling>
<Nullable>enable</Nullable>

Expand Down
2 changes: 1 addition & 1 deletion MarketExtension/Package.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Identity
Name="CostaFotiadis.MarketsExtensionforCommandPalette"
Publisher="CN=3D57AA92-97A9-42D2-8CB0-4207D9145514"
Version="1.0.0.1" />
Version="1.1.0.0" />

<Properties>
<DisplayName>Markets Extension for Command Palette</DisplayName>
Expand Down
15 changes: 15 additions & 0 deletions MarketExtension/Pages/DataSourcesPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions MarketExtension/Pages/PricedListPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions MarketExtension/Pages/SearchPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion MarketExtension/Pages/SymbolDetailPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -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 = """
Expand All @@ -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}" }
]
}
""";
Expand Down
27 changes: 27 additions & 0 deletions MarketExtension/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions MarketExtension/Properties/Resources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@
<data name="Attribution_OpenName" xml:space="preserve">
<value>Elbstream öffnen</value>
</data>
<data name="DataAttribution_Title" xml:space="preserve">
<value>Daten bereitgestellt von {0}</value>
</data>
<data name="DataAttribution_Subtitle" xml:space="preserve">
<value>Kurse können verzögert oder ungenau sein</value>
</data>
<data name="DataAttribution_OpenName" xml:space="preserve">
<value>{0} öffnen</value>
</data>
<data name="Cmd_AddToWatchlist" xml:space="preserve">
<value>Zur Beobachtungsliste hinzufügen</value>
</data>
Expand Down
9 changes: 9 additions & 0 deletions MarketExtension/Properties/Resources.es.resx
Original file line number Diff line number Diff line change
Expand Up @@ -498,4 +498,13 @@
<data name="Currency_KRW" xml:space="preserve">
<value>Won surcoreano (KRW)</value>
</data>
<data name="DataAttribution_Title" xml:space="preserve">
<value>Datos proporcionados por {0}</value>
</data>
<data name="DataAttribution_Subtitle" xml:space="preserve">
<value>Las cotizaciones pueden estar retrasadas o ser inexactas</value>
</data>
<data name="DataAttribution_OpenName" xml:space="preserve">
<value>Abrir {0}</value>
</data>
</root>
9 changes: 9 additions & 0 deletions MarketExtension/Properties/Resources.hi.resx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@
<data name="Attribution_OpenName" xml:space="preserve">
<value>Elbstream खोलें</value>
</data>
<data name="DataAttribution_Title" xml:space="preserve">
<value>डेटा {0} द्वारा प्रदान किया गया</value>
</data>
<data name="DataAttribution_Subtitle" xml:space="preserve">
<value>कीमतें विलंबित या अशुद्ध हो सकती हैं</value>
</data>
<data name="DataAttribution_OpenName" xml:space="preserve">
<value>{0} खोलें</value>
</data>
<data name="Cmd_AddToWatchlist" xml:space="preserve">
<value>वॉचलिस्ट में जोड़ें</value>
</data>
Expand Down
9 changes: 9 additions & 0 deletions MarketExtension/Properties/Resources.ja.resx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,15 @@
<data name="Attribution_OpenName" xml:space="preserve">
<value>Elbstream を開く</value>
</data>
<data name="DataAttribution_Title" xml:space="preserve">
<value>データ提供:{0}</value>
</data>
<data name="DataAttribution_Subtitle" xml:space="preserve">
<value>価格は遅延または不正確な場合があります</value>
</data>
<data name="DataAttribution_OpenName" xml:space="preserve">
<value>{0} を開く</value>
</data>
<data name="Cmd_AddToWatchlist" xml:space="preserve">
<value>ウォッチリストに追加</value>
</data>
Expand Down
9 changes: 9 additions & 0 deletions MarketExtension/Properties/Resources.ko.resx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,15 @@
<data name="Attribution_OpenName" xml:space="preserve">
<value>Elbstream 열기</value>
</data>
<data name="DataAttribution_Title" xml:space="preserve">
<value>데이터 제공: {0}</value>
</data>
<data name="DataAttribution_Subtitle" xml:space="preserve">
<value>시세가 지연되거나 부정확할 수 있습니다</value>
</data>
<data name="DataAttribution_OpenName" xml:space="preserve">
<value>{0} 열기</value>
</data>
<data name="Cmd_AddToWatchlist" xml:space="preserve">
<value>관심목록에 추가</value>
</data>
Expand Down
9 changes: 9 additions & 0 deletions MarketExtension/Properties/Resources.pt-BR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@
<data name="Attribution_OpenName" xml:space="preserve">
<value>Abrir a Elbstream</value>
</data>
<data name="DataAttribution_Title" xml:space="preserve">
<value>Dados fornecidos por {0}</value>
</data>
<data name="DataAttribution_Subtitle" xml:space="preserve">
<value>As cotações podem estar atrasadas ou imprecisas</value>
</data>
<data name="DataAttribution_OpenName" xml:space="preserve">
<value>Abrir {0}</value>
</data>
<data name="Cmd_AddToWatchlist" xml:space="preserve">
<value>Adicionar à lista de acompanhamento</value>
</data>
Expand Down
9 changes: 9 additions & 0 deletions MarketExtension/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,15 @@
<data name="Attribution_OpenName" xml:space="preserve">
<value>Open Elbstream</value>
</data>
<data name="DataAttribution_Title" xml:space="preserve">
<value>Data provided by {0}</value>
</data>
<data name="DataAttribution_Subtitle" xml:space="preserve">
<value>Quotes may be delayed or inaccurate</value>
</data>
<data name="DataAttribution_OpenName" xml:space="preserve">
<value>Open {0}</value>
</data>
<data name="Cmd_AddToWatchlist" xml:space="preserve">
<value>Add to Watchlist</value>
</data>
Expand Down
9 changes: 9 additions & 0 deletions MarketExtension/Properties/Resources.zh-Hans.resx
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,15 @@
<data name="Attribution_OpenName" xml:space="preserve">
<value>打开 Elbstream</value>
</data>
<data name="DataAttribution_Title" xml:space="preserve">
<value>数据由 {0} 提供</value>
</data>
<data name="DataAttribution_Subtitle" xml:space="preserve">
<value>报价可能延迟或不准确</value>
</data>
<data name="DataAttribution_OpenName" xml:space="preserve">
<value>打开 {0}</value>
</data>
<data name="Cmd_AddToWatchlist" xml:space="preserve">
<value>添加到自选</value>
</data>
Expand Down
2 changes: 1 addition & 1 deletion MarketExtension/app.manifest
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.1" name="MarketExtension.app"/>
<assemblyIdentity version="1.1.0.0" name="MarketExtension.app"/>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
Expand Down
2 changes: 1 addition & 1 deletion MarketExtension/build-exe.ps1
Original file line number Diff line number Diff line change
@@ -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")
)

Expand Down
2 changes: 1 addition & 1 deletion MarketExtension/setup-template.iss
Original file line number Diff line number Diff line change
@@ -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}}
Expand Down
37 changes: 37 additions & 0 deletions notes/store-listing.md
Original file line number Diff line number Diff line change
@@ -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".
Loading