feat: project chip + file-wins guard (multi-project stage 2)
When the open flow declares its own top-level project:, show it as a chip in the tab action strip. On mismatch with the host's active workspace, an amber file-wins pill offers a one-click Switch to <project> — the file always wins for the run; switching only aligns the workspace view. New optional FlowEditorPage.activeProject + onSwitchToFileProject. Parse + normalize (mirrors the hub's normalize_project_slug) lives in flow_project.dart, unit-tested. flutter analyze clean, 57 tests green. Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
9196251e00
commit
9e73036d0e
6 changed files with 253 additions and 1 deletions
50
lib/src/flow_project.dart
Normal file
50
lib/src/flow_project.dart
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/// Parsing + normalization for a flow file's own top-level
|
||||
/// `project:` key. Kept separate from the page so the "file wins"
|
||||
/// semantics are unit-testable without pumping the whole editor.
|
||||
///
|
||||
/// Mirrors the hub's `resolve_project` (file's `project:` is the
|
||||
/// run's project unless the caller overrides it) and
|
||||
/// `normalize_project_slug`, so the chip the editor shows compares
|
||||
/// equal to what the hub actually stamps.
|
||||
library;
|
||||
|
||||
/// The flow's own top-level `project:` slug (normalized), or empty
|
||||
/// when the YAML declares none. Only a *non-indented* `project:`
|
||||
/// line counts — a nested `project:` inside a step's `with:` block
|
||||
/// is ignored.
|
||||
String parseFlowProject(String yaml) {
|
||||
for (final raw in yaml.split('\n')) {
|
||||
if (raw.startsWith(' ') || raw.startsWith('\t')) continue;
|
||||
final m = RegExp(r'^project:\s*(.+?)\s*$').firstMatch(raw);
|
||||
if (m != null) {
|
||||
final value = m.group(1)!.replaceAll(RegExp('''^["']|["']\$'''), '');
|
||||
return normalizeProjectSlug(value);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/// Mirror of the hub's `normalize_project_slug`: lowercase,
|
||||
/// collapse spaces/underscores/dashes to a single `-`, drop
|
||||
/// everything else, trim trailing dashes. Empty in → empty out
|
||||
/// (the editor treats "no project" and "blank project" alike).
|
||||
String normalizeProjectSlug(String raw) {
|
||||
final buf = StringBuffer();
|
||||
var prevDash = false;
|
||||
for (final ch in raw.trim().toLowerCase().split('')) {
|
||||
if (RegExp(r'[a-z0-9]').hasMatch(ch)) {
|
||||
buf.write(ch);
|
||||
prevDash = false;
|
||||
} else if ((ch == ' ' || ch == '_' || ch == '-') &&
|
||||
!prevDash &&
|
||||
buf.isNotEmpty) {
|
||||
buf.write('-');
|
||||
prevDash = true;
|
||||
}
|
||||
}
|
||||
var out = buf.toString();
|
||||
while (out.endsWith('-')) {
|
||||
out = out.substring(0, out.length - 1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue