Add in-app help, formula reference outlines, underline and auto-closing

- Help dialog (?, F1, Datei > Hilfe): user topics, all shortcuts and the
  formula functions with one search over everything. Topics and shortcuts
  are plain data in data/help.ts; the function list comes from
  codeReference.ts so new built-ins appear by themselves.
- While a formula is edited, the grid outlines each referenced cell/range
  in the colour of its position in the text, and the outline can be
  dragged to move or resize the reference, as in Excel. Point-mode picks
  (click, drag, arrow keys) are outlined too, so keyboard picking is no
  longer blind.
- Underline as a cell format, plus Mod+Shift+B/I/U toggles that switch on
  unless every selected cell already has the format.
- A formula committed with open parentheses or an open text literal gets
  the missing closers appended; anything that still doesn't parse is left
  alone so the usual error shows instead of a guess.
- Cells and tables with a fill but no text colour get dark or light text
  by WCAG contrast instead of the theme colour (no black on black).
This commit is contained in:
wkh
2026-10-24 00:38:01 +02:00
parent 0368e96e0c
commit 0fca61eb65
19 changed files with 1420 additions and 16 deletions
+21
View File
@@ -4,6 +4,7 @@ import { useDocument } from '../store/document';
import { useTheme } from '../composables/useTheme';
import { useToasts } from '../composables/useToasts';
import BaseModal from './BaseModal.vue';
import HelpModal from './HelpModal.vue';
import CustomFunctionsModal from './CustomFunctionsModal.vue';
import NamedRangesModal from './NamedRangesModal.vue';
import MacrosModal from './MacrosModal.vue';
@@ -120,6 +121,17 @@ const uploadOpen = ref(false);
const loginOpen = ref(false);
const loginReason = ref<string | undefined>();
const accountMenuOpen = ref(false);
const helpOpen = ref(false);
// F1 opens the help from anywhere (also out of a cell editor -- the browser's
// own F1 help is what preventDefault keeps away)
function onHelpKey(e: KeyboardEvent) {
if (e.key !== 'F1') return;
e.preventDefault();
helpOpen.value = true;
}
onMounted(() => window.addEventListener('keydown', onHelpKey));
onBeforeUnmount(() => window.removeEventListener('keydown', onHelpKey));
function openLogin(reason?: string) {
loginReason.value = reason;
@@ -470,10 +482,13 @@ async function onSample() {
<li><button @click="runXlsxExport">Export · Arbeitsmappe (.xlsx)</button></li>
<li><button @click="exportSelectedChartPng">Export · gewähltes Diagramm (.png)</button></li>
<li class="sep" />
<li><button @click="helpOpen = true">Hilfe … (F1)</button></li>
<li><button @click="aboutOpen = true">Über Numolus …</button></li>
</ul>
</div>
<button class="ibtn help" title="Hilfe (F1)" aria-label="Hilfe" @click="helpOpen = true">?</button>
<button
class="ibtn"
:title="`Theme: ${themeLabel} (klicken zum Wechseln)`"
@@ -588,6 +603,7 @@ async function onSample() {
/>
<LoginModal :open="loginOpen" :reason="loginReason" @close="loginOpen = false" />
<UploadModal :open="uploadOpen" @close="uploadOpen = false" />
<HelpModal :open="helpOpen" @close="helpOpen = false" />
<!-- Über / Impressum -->
<BaseModal :open="aboutOpen" title="Über Numolus" @close="aboutOpen = false">
@@ -866,6 +882,11 @@ async function onSample() {
cursor: pointer;
transition: background-color 0.12s ease, color 0.12s ease;
}
/* the help button is a plain "?" rather than an icon */
.ibtn.help {
font-size: 15px;
font-weight: 700;
}
.tgroup.accent .ibtn {
color: var(--accent);
}