Skip to content

Commit c29be22

Browse files
committed
Ground TradingView shortcut profile
1 parent 745f12d commit c29be22

File tree

3 files changed

+215
-38
lines changed

3 files changed

+215
-38
lines changed

scripts/test-bug-fixes.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ test('pine workflow encodes diagnostics and compile-result evidence modes', () =
280280
assert(shortcutProfileContent.includes("'create-alert'"), 'TradingView shortcut profile should define stable alert guidance');
281281
assert(shortcutProfileContent.includes("'drawing-tool-binding'"), 'TradingView shortcut profile should mark drawing bindings as customizable');
282282
assert(shortcutProfileContent.includes("'open-dom-panel'"), 'TradingView shortcut profile should classify DOM shortcuts explicitly');
283+
assert(shortcutProfileContent.includes("'take-snapshot'"), 'TradingView shortcut profile should include grounded reference-only snapshot guidance');
284+
assert(shortcutProfileContent.includes("'add-symbol-to-watchlist'"), 'TradingView shortcut profile should include grounded watchlist shortcut guidance');
285+
assert(shortcutProfileContent.includes('TRADINGVIEW_SHORTCUTS_OFFICIAL_URL'), 'TradingView shortcut profile should record the official support reference');
286+
assert(shortcutProfileContent.includes('TRADINGVIEW_SHORTCUTS_SECONDARY_URL'), 'TradingView shortcut profile should record the secondary Pineify reference');
287+
assert(shortcutProfileContent.includes('resolveTradingViewShortcutId'), 'TradingView shortcut profile should support alias-to-shortcut resolution');
283288
});
284289

285290
test('system prompt includes Pine diagnostics guidance', () => {

scripts/test-tradingview-shortcut-profile.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ const assert = require('assert');
44
const path = require('path');
55

66
const {
7+
TRADINGVIEW_SHORTCUTS_OFFICIAL_URL,
8+
TRADINGVIEW_SHORTCUTS_SECONDARY_URL,
79
buildTradingViewShortcutAction,
810
getTradingViewShortcut,
911
getTradingViewShortcutKey,
1012
listTradingViewShortcuts,
11-
matchesTradingViewShortcutAction
13+
matchesTradingViewShortcutAction,
14+
resolveTradingViewShortcutId
1215
} = require(path.join(__dirname, '..', 'src', 'main', 'tradingview', 'shortcut-profile.js'));
1316

1417
function test(name, fn) {
@@ -25,6 +28,7 @@ function test(name, fn) {
2528
test('stable default TradingView shortcuts are exposed through the profile helper', () => {
2629
const indicatorSearch = getTradingViewShortcut('indicator-search');
2730
const createAlert = getTradingViewShortcut('create-alert');
31+
const quickSearch = getTradingViewShortcut('command palette');
2832

2933
assert(indicatorSearch, 'indicator-search shortcut should exist');
3034
assert.strictEqual(indicatorSearch.key, '/');
@@ -33,6 +37,9 @@ test('stable default TradingView shortcuts are exposed through the profile helpe
3337
assert.strictEqual(createAlert.key, 'alt+a');
3438
assert.strictEqual(createAlert.category, 'stable-default');
3539
assert.strictEqual(getTradingViewShortcutKey('symbol-search'), 'ctrl+k');
40+
assert(quickSearch, 'symbol-search alias should resolve through the profile helper');
41+
assert.strictEqual(quickSearch.id, 'symbol-search');
42+
assert.strictEqual(quickSearch.surface, 'quick-search');
3643
});
3744

3845
test('drawing shortcuts are marked customizable rather than universal', () => {
@@ -64,11 +71,36 @@ test('buildTradingViewShortcutAction preserves shortcut metadata for workflow ac
6471
assert.strictEqual(action.key, '/');
6572
assert.strictEqual(action.tradingViewShortcut.id, 'indicator-search');
6673
assert.strictEqual(action.tradingViewShortcut.category, 'stable-default');
74+
assert.strictEqual(action.tradingViewShortcut.surface, 'indicator-search');
6775
assert(matchesTradingViewShortcutAction(action, 'indicator-search'));
6876
});
6977

7078
test('listTradingViewShortcuts returns the categorized TradingView profile inventory', () => {
7179
const shortcuts = listTradingViewShortcuts();
7280
assert(Array.isArray(shortcuts), 'shortcut inventory should be an array');
73-
assert(shortcuts.length >= 6, 'shortcut inventory should include the core TradingView shortcuts');
81+
assert(shortcuts.length >= 12, 'shortcut inventory should include the grounded TradingView shortcut inventory');
82+
});
83+
84+
test('shortcut profile exposes reference-only chart shortcuts with source provenance', () => {
85+
const snapshot = getTradingViewShortcut('take snapshot');
86+
const watchlist = getTradingViewShortcut('add-symbol-to-watchlist');
87+
88+
assert(snapshot, 'snapshot shortcut should resolve by alias');
89+
assert.strictEqual(snapshot.key, 'alt+s');
90+
assert.strictEqual(snapshot.category, 'reference-only');
91+
assert.strictEqual(snapshot.sourceConfidence, 'secondary-reference');
92+
assert(snapshot.sourceUrls.includes(TRADINGVIEW_SHORTCUTS_SECONDARY_URL));
93+
assert(watchlist, 'watchlist shortcut should exist');
94+
assert.strictEqual(watchlist.key, 'alt+w');
95+
assert.strictEqual(watchlist.surface, 'watchlist');
96+
});
97+
98+
test('shortcut profile resolves aliases and documents official shortcut references', () => {
99+
assert.strictEqual(resolveTradingViewShortcutId('command palette'), 'symbol-search');
100+
assert.strictEqual(resolveTradingViewShortcutId('quick search'), 'symbol-search');
101+
assert.strictEqual(resolveTradingViewShortcutId('new alert'), 'create-alert');
102+
103+
const indicatorSearch = getTradingViewShortcut('indicator-search');
104+
assert(indicatorSearch.sourceUrls.includes(TRADINGVIEW_SHORTCUTS_OFFICIAL_URL));
105+
assert(indicatorSearch.sourceUrls.includes(TRADINGVIEW_SHORTCUTS_SECONDARY_URL));
74106
});
Lines changed: 176 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,247 @@
1+
const TRADINGVIEW_SHORTCUTS_OFFICIAL_URL = 'https://www.tradingview.com/support/shortcuts/';
2+
const TRADINGVIEW_SHORTCUTS_SECONDARY_URL = 'https://pineify.app/resources/blog/tradingview-hotkeys-the-complete-2025-guide-to-faster-charting-and-execution';
3+
14
function cloneShortcut(shortcut) {
25
if (!shortcut || typeof shortcut !== 'object') return null;
36
return {
47
...shortcut,
58
aliases: Array.isArray(shortcut.aliases) ? [...shortcut.aliases] : [],
6-
notes: Array.isArray(shortcut.notes) ? [...shortcut.notes] : []
9+
notes: Array.isArray(shortcut.notes) ? [...shortcut.notes] : [],
10+
platforms: Array.isArray(shortcut.platforms) ? [...shortcut.platforms] : [],
11+
sourceUrls: Array.isArray(shortcut.sourceUrls) ? [...shortcut.sourceUrls] : []
712
};
813
}
914

15+
function createShortcut(definition) {
16+
return Object.freeze({
17+
...definition,
18+
aliases: Object.freeze(Array.isArray(definition.aliases) ? definition.aliases : []),
19+
notes: Object.freeze(Array.isArray(definition.notes) ? definition.notes : []),
20+
platforms: Object.freeze(Array.isArray(definition.platforms) ? definition.platforms : ['windows', 'linux', 'mac']),
21+
sourceUrls: Object.freeze(Array.isArray(definition.sourceUrls) ? definition.sourceUrls : []),
22+
sourceConfidence: definition.sourceConfidence || 'internal-profile'
23+
});
24+
}
25+
26+
const OFFICIAL_AND_SECONDARY_SOURCES = Object.freeze([
27+
TRADINGVIEW_SHORTCUTS_OFFICIAL_URL,
28+
TRADINGVIEW_SHORTCUTS_SECONDARY_URL
29+
]);
30+
31+
const SECONDARY_REFERENCE_ONLY_SOURCES = Object.freeze([
32+
TRADINGVIEW_SHORTCUTS_SECONDARY_URL
33+
]);
34+
1035
const TRADINGVIEW_SHORTCUTS = Object.freeze({
11-
'indicator-search': Object.freeze({
36+
'indicator-search': createShortcut({
1237
id: 'indicator-search',
1338
key: '/',
1439
category: 'stable-default',
1540
surface: 'indicator-search',
1641
safety: 'safe',
17-
aliases: Object.freeze(['indicator search', 'study search']),
18-
notes: Object.freeze(['Stable default TradingView search opener for indicators and studies when the chart surface is verified.'])
42+
aliases: ['indicator search', 'study search', 'indicators menu', 'open indicators'],
43+
notes: ['Stable default TradingView search opener for indicators and studies when the chart surface is verified.'],
44+
sourceConfidence: 'official-and-secondary',
45+
sourceUrls: OFFICIAL_AND_SECONDARY_SOURCES
1946
}),
20-
'create-alert': Object.freeze({
47+
'create-alert': createShortcut({
2148
id: 'create-alert',
2249
key: 'alt+a',
2350
category: 'stable-default',
2451
surface: 'create-alert',
2552
safety: 'safe',
26-
aliases: Object.freeze(['alert dialog', 'create alert']),
27-
notes: Object.freeze(['Stable default TradingView shortcut for opening the Create Alert dialog.'])
53+
aliases: ['alert dialog', 'create alert', 'new alert'],
54+
notes: ['Stable default TradingView shortcut for opening the Create Alert dialog.'],
55+
sourceConfidence: 'official-and-secondary',
56+
sourceUrls: OFFICIAL_AND_SECONDARY_SOURCES
2857
}),
29-
'symbol-search': Object.freeze({
58+
'symbol-search': createShortcut({
3059
id: 'symbol-search',
3160
key: 'ctrl+k',
3261
category: 'stable-default',
33-
surface: 'symbol-search',
62+
surface: 'quick-search',
3463
safety: 'safe',
35-
aliases: Object.freeze(['symbol search']),
36-
notes: Object.freeze(['Treat as TradingView-specific tool knowledge rather than a generic desktop shortcut.'])
64+
aliases: ['symbol search', 'quick search', 'command palette', 'search symbols'],
65+
notes: ['Treat as TradingView-specific tool knowledge rather than a generic desktop shortcut.'],
66+
sourceConfidence: 'secondary-reference',
67+
sourceUrls: SECONDARY_REFERENCE_ONLY_SOURCES
3768
}),
38-
'dismiss-surface': Object.freeze({
69+
'dismiss-surface': createShortcut({
3970
id: 'dismiss-surface',
4071
key: 'esc',
4172
category: 'stable-default',
4273
surface: 'dismiss-surface',
4374
safety: 'safe',
44-
aliases: Object.freeze(['dismiss', 'close popup']),
45-
notes: Object.freeze(['Useful for dismissing dialogs or search surfaces when TradingView focus is verified.'])
75+
aliases: ['dismiss', 'close popup', 'close dialog'],
76+
notes: ['Useful for dismissing dialogs or search surfaces when TradingView focus is verified.'],
77+
sourceConfidence: 'official-page-family',
78+
sourceUrls: [TRADINGVIEW_SHORTCUTS_OFFICIAL_URL]
4679
}),
47-
'open-pine-editor': Object.freeze({
80+
'open-pine-editor': createShortcut({
4881
id: 'open-pine-editor',
4982
key: 'ctrl+e',
5083
category: 'context-dependent',
5184
surface: 'pine-editor',
5285
safety: 'safe',
53-
aliases: Object.freeze(['pine editor', 'open pine editor']),
54-
notes: Object.freeze(['Requires verified TradingView focus and should not be treated as a universal desktop shortcut.'])
86+
aliases: ['pine editor', 'open pine editor'],
87+
notes: ['Requires verified TradingView focus and should not be treated as a universal desktop shortcut.'],
88+
sourceConfidence: 'internal-profile',
89+
sourceUrls: [TRADINGVIEW_SHORTCUTS_OFFICIAL_URL]
5590
}),
56-
'open-object-tree': Object.freeze({
91+
'open-object-tree': createShortcut({
5792
id: 'open-object-tree',
5893
key: 'ctrl+shift+o',
5994
category: 'context-dependent',
6095
surface: 'object-tree',
6196
safety: 'safe',
62-
aliases: Object.freeze(['object tree']),
63-
notes: Object.freeze(['Treat as TradingView-specific and verify the resulting surface before typing.'])
97+
aliases: ['object tree'],
98+
notes: ['Treat as TradingView-specific and verify the resulting surface before typing.'],
99+
sourceConfidence: 'internal-profile',
100+
sourceUrls: [TRADINGVIEW_SHORTCUTS_OFFICIAL_URL]
64101
}),
65-
'drawing-tool-binding': Object.freeze({
102+
'drawing-tool-binding': createShortcut({
66103
id: 'drawing-tool-binding',
67104
key: null,
68105
category: 'customizable',
69106
surface: 'drawing-tool',
70107
safety: 'safe',
71-
aliases: Object.freeze(['trend line shortcut', 'drawing shortcut']),
72-
notes: Object.freeze(['Drawing tool bindings may be user-customized and should be treated as unknown until confirmed.'])
108+
aliases: ['trend line shortcut', 'drawing shortcut', 'drawing tool shortcut'],
109+
notes: ['Drawing tool bindings may be user-customized and should be treated as unknown until confirmed.'],
110+
sourceConfidence: 'official-page-family',
111+
sourceUrls: [TRADINGVIEW_SHORTCUTS_OFFICIAL_URL]
73112
}),
74-
'open-dom-panel': Object.freeze({
113+
'open-dom-panel': createShortcut({
75114
id: 'open-dom-panel',
76115
key: 'ctrl+d',
77116
category: 'context-dependent',
78117
surface: 'dom-panel',
79118
safety: 'paper-test-only',
80-
aliases: Object.freeze(['depth of market', 'dom']),
81-
notes: Object.freeze(['Treat Trading Panel and DOM shortcuts as app-specific and advisory-safe only.'])
119+
aliases: ['depth of market', 'dom'],
120+
notes: ['Treat Trading Panel and DOM shortcuts as app-specific and advisory-safe only.'],
121+
sourceConfidence: 'internal-profile',
122+
sourceUrls: [TRADINGVIEW_SHORTCUTS_OFFICIAL_URL]
82123
}),
83-
'open-paper-trading': Object.freeze({
124+
'open-paper-trading': createShortcut({
84125
id: 'open-paper-trading',
85126
key: 'alt+t',
86127
category: 'context-dependent',
87128
surface: 'paper-trading-panel',
88129
safety: 'paper-test-only',
89-
aliases: Object.freeze(['paper trading']),
90-
notes: Object.freeze(['Paper Trading shortcuts should remain bounded to verified paper-assist flows.'])
130+
aliases: ['paper trading', 'paper account'],
131+
notes: ['Paper Trading shortcuts should remain bounded to verified paper-assist flows.'],
132+
sourceConfidence: 'internal-profile',
133+
sourceUrls: [TRADINGVIEW_SHORTCUTS_OFFICIAL_URL]
134+
}),
135+
'save-layout': createShortcut({
136+
id: 'save-layout',
137+
key: 'ctrl+s',
138+
category: 'reference-only',
139+
surface: 'layout',
140+
safety: 'safe',
141+
aliases: ['save your layout', 'save layout'],
142+
notes: ['Useful reference shortcut for layout management, but not currently routed into automated workflows.'],
143+
sourceConfidence: 'secondary-reference',
144+
sourceUrls: SECONDARY_REFERENCE_ONLY_SOURCES
145+
}),
146+
'load-layout': createShortcut({
147+
id: 'load-layout',
148+
key: '.',
149+
category: 'reference-only',
150+
surface: 'layout',
151+
safety: 'safe',
152+
aliases: ['load layout', 'open saved layout', 'saved layout'],
153+
notes: ['Reference-only layout shortcut from secondary guidance; keep automation usage explicit and verified.'],
154+
sourceConfidence: 'secondary-reference',
155+
sourceUrls: SECONDARY_REFERENCE_ONLY_SOURCES
156+
}),
157+
'take-snapshot': createShortcut({
158+
id: 'take-snapshot',
159+
key: 'alt+s',
160+
category: 'reference-only',
161+
surface: 'chart-capture',
162+
safety: 'safe',
163+
aliases: ['snapshot', 'take snapshot', 'chart snapshot'],
164+
notes: ['Reference-only chart capture shortcut; prefer existing bounded screenshot flows for automation.'],
165+
sourceConfidence: 'secondary-reference',
166+
sourceUrls: SECONDARY_REFERENCE_ONLY_SOURCES
167+
}),
168+
'reset-chart-zoom': createShortcut({
169+
id: 'reset-chart-zoom',
170+
key: 'alt+r',
171+
category: 'reference-only',
172+
surface: 'chart-view',
173+
safety: 'safe',
174+
aliases: ['reset chart zoom', 'reset zoom'],
175+
notes: ['Reference-only chart view shortcut from secondary guidance.'],
176+
sourceConfidence: 'secondary-reference',
177+
sourceUrls: SECONDARY_REFERENCE_ONLY_SOURCES
178+
}),
179+
'add-symbol-to-watchlist': createShortcut({
180+
id: 'add-symbol-to-watchlist',
181+
key: 'alt+w',
182+
category: 'reference-only',
183+
surface: 'watchlist',
184+
safety: 'safe',
185+
aliases: ['add to watchlist', 'watchlist shortcut', 'watchlist'],
186+
notes: ['Reference-only watchlist shortcut from secondary guidance; explicit verification should precede any automated follow-up typing.'],
187+
sourceConfidence: 'secondary-reference',
188+
sourceUrls: SECONDARY_REFERENCE_ONLY_SOURCES
189+
}),
190+
'invert-chart': createShortcut({
191+
id: 'invert-chart',
192+
key: 'alt+i',
193+
category: 'reference-only',
194+
surface: 'chart-view',
195+
safety: 'safe',
196+
aliases: ['invert chart'],
197+
notes: ['Reference-only chart view shortcut from secondary guidance.'],
198+
sourceConfidence: 'secondary-reference',
199+
sourceUrls: SECONDARY_REFERENCE_ONLY_SOURCES
200+
}),
201+
'enter-full-screen': createShortcut({
202+
id: 'enter-full-screen',
203+
key: 'f11',
204+
category: 'reference-only',
205+
surface: 'chart-view',
206+
safety: 'safe',
207+
aliases: ['full screen', 'fullscreen'],
208+
notes: ['Reference-only view shortcut; use only when fullscreen transitions are explicitly requested and safe.'],
209+
sourceConfidence: 'secondary-reference',
210+
sourceUrls: SECONDARY_REFERENCE_ONLY_SOURCES
91211
})
92212
});
93213

94214
function listTradingViewShortcuts() {
95215
return Object.values(TRADINGVIEW_SHORTCUTS).map(cloneShortcut);
96216
}
97217

218+
function normalizeKey(value) {
219+
return String(value || '').trim().toLowerCase();
220+
}
221+
222+
function resolveTradingViewShortcutId(value) {
223+
const normalized = normalizeKey(value);
224+
if (!normalized) return null;
225+
if (TRADINGVIEW_SHORTCUTS[normalized]) return normalized;
226+
227+
const match = Object.values(TRADINGVIEW_SHORTCUTS).find((shortcut) =>
228+
normalizeKey(shortcut.id) === normalized
229+
|| normalizeKey(shortcut.surface) === normalized
230+
|| (Array.isArray(shortcut.aliases) && shortcut.aliases.some((alias) => normalizeKey(alias) === normalized))
231+
);
232+
233+
return match?.id || null;
234+
}
235+
98236
function getTradingViewShortcut(id) {
99-
return cloneShortcut(TRADINGVIEW_SHORTCUTS[String(id || '').trim().toLowerCase()] || null);
237+
const resolvedId = resolveTradingViewShortcutId(id);
238+
return cloneShortcut(resolvedId ? TRADINGVIEW_SHORTCUTS[resolvedId] : null);
100239
}
101240

102241
function getTradingViewShortcutKey(id) {
103242
return getTradingViewShortcut(id)?.key || null;
104243
}
105244

106-
function normalizeKey(value) {
107-
return String(value || '').trim().toLowerCase();
108-
}
109-
110245
function matchesTradingViewShortcutAction(action, id) {
111246
if (!action || typeof action !== 'object') return false;
112247
if (String(action.type || '').trim().toLowerCase() !== 'key') return false;
@@ -124,16 +259,21 @@ function buildTradingViewShortcutAction(id, overrides = {}) {
124259
tradingViewShortcut: {
125260
id: shortcut.id,
126261
category: shortcut.category,
127-
safety: shortcut.safety
262+
surface: shortcut.surface,
263+
safety: shortcut.safety,
264+
sourceConfidence: shortcut.sourceConfidence
128265
},
129266
...overrides
130267
};
131268
}
132269

133270
module.exports = {
271+
TRADINGVIEW_SHORTCUTS_OFFICIAL_URL,
272+
TRADINGVIEW_SHORTCUTS_SECONDARY_URL,
134273
buildTradingViewShortcutAction,
135274
getTradingViewShortcut,
136275
getTradingViewShortcutKey,
137276
listTradingViewShortcuts,
138-
matchesTradingViewShortcutAction
277+
matchesTradingViewShortcutAction,
278+
resolveTradingViewShortcutId
139279
};

0 commit comments

Comments
 (0)