feat(editor): localised analyzer/strip/run messages + monospace font fallback
Closes operator-reported issues around the text-tab font and
the English-only run + diagnostic surfaces.
- **Real monospace everywhere**. The package never bundled
JetBrains Mono as an asset (it relied on Studio's
google_fonts pre-load), so a host that doesn't ship the
font saw the YAML editor render in the system proportional
default. New _monoTextStyle() pins fontFamilyFallback to a
cross-platform monospace chain (Menlo / Consolas / Courier
New / monospace) so the editor stays a grid in every host.
Applied to the code field, gutter, diagnostic strip,
issue rows, hover card, fix buttons, and the run tab's
error box.
- **Locale-aware analyzer messages**. New AnalyzerStrings
adapter holds every string the analyzer emits, with an
.english default + an .from(FlowEditorStrings) factory.
FlowYamlCodeController.setCapabilityProviders takes the
strings; FlowEditorPage wires them from the active locale.
The analyzer's 'Unknown capability', 'Did you mean',
'Not in the store — install locally with fai install
--link', 'Unknown input/output type', YAML parse errors,
and every quick-fix label (Install / Add source / Use /
Change to) now flip to DE when the editor's locale is DE.
- **Localised run + diagnostic chrome**. The bottom strip's
'No issues', '2 errors · 1 warning', 'Copy all'; the
issue row's L-prefix + Copy tooltip; the hover card's
L-prefix + Copy tooltip; the run-block tooltip + inline
message ('2 Fehler verhindern den Lauf · siehe
Diagnose-Leiste unten'); the step-row 'awaiting approval'
suffix; the CopyableErrorBox's Copy / Copied tooltip —
all now flow through FlowEditorStrings.
Bumped to 0.20.0.
Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
b6bb8741a9
commit
5cd2745db5
6 changed files with 306 additions and 97 deletions
|
|
@ -137,6 +137,7 @@ class _FlowEditorPageState extends State<FlowEditorPage>
|
|||
_controller.codeController.setCapabilityProviders(
|
||||
available: () => widget.availableCapabilities,
|
||||
store: () => widget.storeCapabilities,
|
||||
strings: AnalyzerStrings.from(_l),
|
||||
);
|
||||
_controller.addListener(_onCtrlChanged);
|
||||
_controller.codeController.hoverRequest.addListener(_onHoverChanged);
|
||||
|
|
@ -175,6 +176,7 @@ class _FlowEditorPageState extends State<FlowEditorPage>
|
|||
_hoverEntry = OverlayEntry(
|
||||
builder: (ctx) => _IssueHoverCard(
|
||||
request: req,
|
||||
strings: _l,
|
||||
onApplyFix: _applyQuickFix,
|
||||
onEnter: _controller.codeController.cancelHoverDismiss,
|
||||
onExit: _controller.codeController.scheduleHoverDismiss,
|
||||
|
|
@ -205,6 +207,7 @@ class _FlowEditorPageState extends State<FlowEditorPage>
|
|||
_controller.codeController.setCapabilityProviders(
|
||||
available: () => newCaps,
|
||||
store: () => widget.storeCapabilities,
|
||||
strings: AnalyzerStrings.from(_l),
|
||||
);
|
||||
await _controller.codeController.reanalyze();
|
||||
}
|
||||
|
|
@ -217,6 +220,7 @@ class _FlowEditorPageState extends State<FlowEditorPage>
|
|||
_controller.codeController.setCapabilityProviders(
|
||||
available: () => newCaps,
|
||||
store: () => widget.storeCapabilities,
|
||||
strings: AnalyzerStrings.from(_l),
|
||||
);
|
||||
await _controller.codeController.reanalyze();
|
||||
}
|
||||
|
|
@ -578,6 +582,7 @@ outputs:
|
|||
// the message + quick-fix buttons.
|
||||
_DiagnosticStrip(
|
||||
controller: _controller.codeController,
|
||||
strings: _l,
|
||||
onApplyFix: _applyQuickFix,
|
||||
),
|
||||
],
|
||||
|
|
@ -719,9 +724,15 @@ outputs:
|
|||
}
|
||||
|
||||
Widget _textTab(ThemeData theme) {
|
||||
final mono = TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 13,
|
||||
// The editor itself doesn't bundle JetBrains Mono as an
|
||||
// asset (Studio preloads it via google_fonts). When the
|
||||
// host isn't Studio, Flutter would fall back to a
|
||||
// proportional default and the code would line up like
|
||||
// ransom-note prose. _monoTextStyle's fontFamilyFallback
|
||||
// pins the chain to the system monospace stack so a YAML
|
||||
// grid stays a grid in every host.
|
||||
final mono = _monoTextStyle(
|
||||
size: 13,
|
||||
height: 1.45,
|
||||
color: theme.colorScheme.onSurface,
|
||||
);
|
||||
|
|
@ -757,11 +768,7 @@ outputs:
|
|||
Map<String, TextStyle> _yamlStyle(ThemeData theme) {
|
||||
final cs = theme.colorScheme;
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
final monoBase = TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 13,
|
||||
height: 1.45,
|
||||
);
|
||||
final monoBase = _monoTextStyle(size: 13, height: 1.45);
|
||||
// Pick clearly-distinguished hues so keys, strings,
|
||||
// numbers, anchors, and comments don't blur into each
|
||||
// other. We derive accents from the active ColorScheme
|
||||
|
|
@ -1002,8 +1009,7 @@ class _TabActionStrip extends StatelessWidget {
|
|||
const SizedBox(width: FaiSpace.sm),
|
||||
Tooltip(
|
||||
message: errorCount > 0
|
||||
? '$errorCount error${errorCount == 1 ? '' : 's'} '
|
||||
'prevent the run — fix them first'
|
||||
? strings.runErrorsBlockTooltip(errorCount)
|
||||
: '',
|
||||
child: FilledButton.icon(
|
||||
onPressed: onRun,
|
||||
|
|
@ -1277,11 +1283,44 @@ class _NewFlowDialogState extends State<_NewFlowDialog> {
|
|||
/// the trackpad-only operator. When the analyzer attaches a
|
||||
/// `QuickFix` to an issue, the row also renders an action
|
||||
/// button (e.g. "Install <cap>" or "Change to \"bytes\"").
|
||||
/// Build a TextStyle that prefers `JetBrains Mono` (Studio
|
||||
/// preloads it via `google_fonts`) but falls back to the
|
||||
/// system's generic `monospace` family when the bundled font
|
||||
/// isn't registered — which is what happens when this package
|
||||
/// is hosted by something other than Studio. Without the
|
||||
/// fallback the field rendered with the default proportional
|
||||
/// font and code lined up like ransom-note prose.
|
||||
TextStyle _monoTextStyle({
|
||||
required double size,
|
||||
Color? color,
|
||||
FontWeight? weight,
|
||||
double? letterSpacing,
|
||||
double? height,
|
||||
}) {
|
||||
return TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontFamilyFallback: const [
|
||||
'JetBrainsMono Nerd Font',
|
||||
'Menlo',
|
||||
'Consolas',
|
||||
'Courier New',
|
||||
'monospace',
|
||||
],
|
||||
fontSize: size,
|
||||
color: color,
|
||||
fontWeight: weight,
|
||||
letterSpacing: letterSpacing,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
|
||||
class _DiagnosticStrip extends StatefulWidget {
|
||||
final FlowYamlCodeController controller;
|
||||
final FlowEditorStrings strings;
|
||||
final Future<void> Function(QuickFix) onApplyFix;
|
||||
const _DiagnosticStrip({
|
||||
required this.controller,
|
||||
required this.strings,
|
||||
required this.onApplyFix,
|
||||
});
|
||||
|
||||
|
|
@ -1324,7 +1363,9 @@ class _DiagnosticStripState extends State<_DiagnosticStrip> {
|
|||
String _formatAll(List<Issue> issues) {
|
||||
final buf = StringBuffer();
|
||||
for (final i in issues) {
|
||||
buf.writeln('L${i.line + 1}: ${i.message}');
|
||||
buf.writeln(
|
||||
'${widget.strings.diagnosticLinePrefix(i.line + 1)}: ${i.message}',
|
||||
);
|
||||
}
|
||||
return buf.toString().trimRight();
|
||||
}
|
||||
|
|
@ -1354,12 +1395,11 @@ class _DiagnosticStripState extends State<_DiagnosticStrip> {
|
|||
),
|
||||
),
|
||||
child: Text(
|
||||
'No issues',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 10,
|
||||
letterSpacing: 0.4,
|
||||
widget.strings.diagnosticNoIssues,
|
||||
style: _monoTextStyle(
|
||||
size: 10,
|
||||
color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.6),
|
||||
letterSpacing: 0.4,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -1402,27 +1442,25 @@ class _DiagnosticStripState extends State<_DiagnosticStrip> {
|
|||
const SizedBox(width: 8),
|
||||
Text(
|
||||
_summary(errorCount, warnCount),
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
style: _monoTextStyle(
|
||||
size: 11,
|
||||
weight: FontWeight.w600,
|
||||
color: tone,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: SelectableText(
|
||||
'L${issues.first.line + 1}: ${issues.first.message}',
|
||||
'${widget.strings.diagnosticLinePrefix(issues.first.line + 1)}: ${issues.first.message}',
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 11,
|
||||
style: _monoTextStyle(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'Copy all',
|
||||
tooltip: widget.strings.diagnosticCopyAll,
|
||||
icon: const Icon(Icons.content_copy, size: 14),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () => _copy(_formatAll(issues)),
|
||||
|
|
@ -1453,10 +1491,12 @@ class _DiagnosticStripState extends State<_DiagnosticStrip> {
|
|||
issue: issue,
|
||||
fixes: widget.controller.fixesFor(issue),
|
||||
tone: _toneForIssue(issue.type, theme),
|
||||
strings: widget.strings,
|
||||
busy: _busy,
|
||||
onApplyFix: _runFix,
|
||||
onCopy: () =>
|
||||
_copy('L${issue.line + 1}: ${issue.message}'),
|
||||
onCopy: () => _copy(
|
||||
'${widget.strings.diagnosticLinePrefix(issue.line + 1)}: ${issue.message}',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -1471,9 +1511,9 @@ class _DiagnosticStripState extends State<_DiagnosticStrip> {
|
|||
|
||||
String _summary(int errors, int warnings) {
|
||||
final parts = <String>[];
|
||||
if (errors > 0) parts.add('$errors error${errors == 1 ? '' : 's'}');
|
||||
if (errors > 0) parts.add(widget.strings.diagnosticErrors(errors));
|
||||
if (warnings > 0) {
|
||||
parts.add('$warnings warning${warnings == 1 ? '' : 's'}');
|
||||
parts.add(widget.strings.diagnosticWarnings(warnings));
|
||||
}
|
||||
return parts.join(' · ');
|
||||
}
|
||||
|
|
@ -1487,6 +1527,7 @@ class _IssueRow extends StatelessWidget {
|
|||
final Issue issue;
|
||||
final List<QuickFix> fixes;
|
||||
final Color tone;
|
||||
final FlowEditorStrings strings;
|
||||
final Map<QuickFix, bool> busy;
|
||||
final Future<void> Function(QuickFix) onApplyFix;
|
||||
final VoidCallback onCopy;
|
||||
|
|
@ -1495,6 +1536,7 @@ class _IssueRow extends StatelessWidget {
|
|||
required this.issue,
|
||||
required this.fixes,
|
||||
required this.tone,
|
||||
required this.strings,
|
||||
required this.busy,
|
||||
required this.onApplyFix,
|
||||
required this.onCopy,
|
||||
|
|
@ -1517,10 +1559,9 @@ class _IssueRow extends StatelessWidget {
|
|||
SizedBox(
|
||||
width: 42,
|
||||
child: Text(
|
||||
'L${issue.line + 1}',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 11,
|
||||
strings.diagnosticLinePrefix(issue.line + 1),
|
||||
style: _monoTextStyle(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
|
|
@ -1528,16 +1569,15 @@ class _IssueRow extends StatelessWidget {
|
|||
Expanded(
|
||||
child: SelectableText(
|
||||
issue.message,
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 11,
|
||||
style: _monoTextStyle(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
tooltip: 'Copy',
|
||||
tooltip: strings.runCopy,
|
||||
icon: const Icon(Icons.content_copy, size: 13),
|
||||
visualDensity: VisualDensity.compact,
|
||||
padding: EdgeInsets.zero,
|
||||
|
|
@ -1557,10 +1597,7 @@ class _IssueRow extends StatelessWidget {
|
|||
: const Icon(Icons.auto_fix_high, size: 13),
|
||||
label: Text(
|
||||
fix.label,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 11,
|
||||
),
|
||||
style: _monoTextStyle(size: 11),
|
||||
),
|
||||
style: FilledButton.styleFrom(
|
||||
visualDensity: VisualDensity.compact,
|
||||
|
|
@ -1586,12 +1623,14 @@ class _IssueRow extends StatelessWidget {
|
|||
/// tooltip so action buttons stay clickable.
|
||||
class _IssueHoverCard extends StatelessWidget {
|
||||
final IssueHoverRequest request;
|
||||
final FlowEditorStrings strings;
|
||||
final Future<void> Function(QuickFix) onApplyFix;
|
||||
final VoidCallback onEnter;
|
||||
final VoidCallback onExit;
|
||||
|
||||
const _IssueHoverCard({
|
||||
required this.request,
|
||||
required this.strings,
|
||||
required this.onApplyFix,
|
||||
required this.onEnter,
|
||||
required this.onExit,
|
||||
|
|
@ -1666,10 +1705,9 @@ class _IssueHoverCard extends StatelessWidget {
|
|||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'L${request.line + 1}',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 11,
|
||||
strings.diagnosticLinePrefix(request.line + 1),
|
||||
style: _monoTextStyle(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
|
|
@ -1677,22 +1715,21 @@ class _IssueHoverCard extends StatelessWidget {
|
|||
Expanded(
|
||||
child: SelectableText(
|
||||
request.message,
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 12,
|
||||
style: _monoTextStyle(
|
||||
size: 12,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'Copy',
|
||||
tooltip: strings.runCopy,
|
||||
icon: const Icon(Icons.content_copy, size: 14),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(
|
||||
text:
|
||||
'L${request.line + 1}: ${request.message}',
|
||||
'${strings.diagnosticLinePrefix(request.line + 1)}: ${request.message}',
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -1711,10 +1748,7 @@ class _IssueHoverCard extends StatelessWidget {
|
|||
icon: const Icon(Icons.auto_fix_high, size: 13),
|
||||
label: Text(
|
||||
fix.label,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
fontSize: 11,
|
||||
),
|
||||
style: _monoTextStyle(size: 11),
|
||||
),
|
||||
style: FilledButton.styleFrom(
|
||||
visualDensity: VisualDensity.compact,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue