blob: e0b3f3993e02ccadfdc93b391184d003a638afa8 [file]
// Copyright (C) 2026 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import m from 'mithril';
import type {Hotkey} from '../../base/hotkeys';
import {HotkeyGlyphs, Keycap} from '../../widgets/hotkey_glyphs';
import {showModal} from '../../widgets/modal';
import {nodeRegistry} from './query_builder/node_registry';
interface HelpEntry {
readonly keys: m.Children;
readonly description: string;
}
interface HelpSection {
readonly title: string;
readonly entries: ReadonlyArray<HelpEntry>;
}
export function showHelp() {
return showModal({
title: 'Data Explorer Help',
content: () => m(HelpContent),
});
}
function keycap(glyph: m.Children): m.Children {
return m(Keycap, {spacing: 'large'}, glyph);
}
function hotkey(combo: Hotkey): m.Children {
return m(HotkeyGlyphs, {spacing: 'large', hotkey: combo});
}
function getNodeCreationEntries(): HelpEntry[] {
return nodeRegistry
.list()
.filter(([_, desc]) => desc.type === 'source' && desc.hotkey)
.sort((a, b) => a[1].name.localeCompare(b[1].name))
.flatMap(([_, desc]) => {
const hk = desc.hotkey;
if (hk === undefined) return [];
return {
keys: keycap(hk.toUpperCase()),
description: `Add ${desc.name} node`,
};
});
}
function getHelpSections(): HelpSection[] {
return [
{
title: 'Node Creation',
entries: getNodeCreationEntries(),
},
{
title: 'Graph Editing',
entries: [
{
keys: [keycap('Delete'), ' / ', keycap('Backspace')],
description: 'Delete selected node(s)',
},
{keys: hotkey('Mod+C'), description: 'Copy selected node(s)'},
{keys: hotkey('Mod+V'), description: 'Paste copied node(s)'},
],
},
{
title: 'Undo / Redo',
entries: [
{keys: hotkey('Mod+Z'), description: 'Undo'},
{keys: hotkey('Mod+Shift+Z'), description: 'Redo'},
{keys: hotkey('Mod+Y'), description: 'Redo (alternative)'},
],
},
{
title: 'Query Execution',
entries: [
{keys: hotkey('Mod+Enter'), description: 'Execute selected node'},
],
},
{
title: 'Import / Export',
entries: [
{keys: keycap('I'), description: 'Import graph from JSON file'},
{keys: keycap('E'), description: 'Export graph to JSON file'},
],
},
{
title: 'Navigation',
entries: [
{
keys: [keycap('W'), keycap('A'), keycap('S'), keycap('D')],
description: 'Pan canvas',
},
{keys: 'Scroll', description: 'Pan canvas'},
{
keys: [keycap('Ctrl'), ' / ', keycap('Cmd'), ' + Scroll'],
description: 'Zoom in/out',
},
],
},
{
title: 'Selection',
entries: [
{keys: 'Click', description: 'Select node'},
{
keys: [keycap('Ctrl'), ' / ', keycap('Cmd'), ' + Click'],
description: 'Toggle node in selection',
},
{
keys: [keycap('Shift'), ' + Click'],
description: 'Add node to selection',
},
{
keys: [keycap('Shift'), ' + Drag'],
description: 'Rectangle select',
},
{keys: 'Click canvas', description: 'Deselect all'},
{keys: 'Drag node', description: 'Move node'},
],
},
];
}
function renderSection(section: HelpSection): m.Children {
return [
m('h2', section.title),
m(
'table',
section.entries.map((entry) =>
m('tr', m('td', entry.keys), m('td', entry.description)),
),
),
];
}
class HelpContent implements m.ClassComponent {
view(): m.Children {
return m('.pf-help-modal', getHelpSections().map(renderSection));
}
}