fix(store,audit,workspace): usertest low-findings tail (0.72.1)
Some checks failed
Security / Security check (push) Failing after 1s
Some checks failed
Security / Security check (push) Failing after 1s
- Store hero: 'TODAY' badge only for an operator-accepted story; the rotating compiled-in fallback deck now says 'FEATURED' (no false freshness claim). Policy as a top-level function with unit tests. - Audit filter chips: standard label typography instead of mono — mono stays reserved for paths and identifiers. - DE chain wording: 'Hash-Kette geprüft' as the one confirmation term (audit header now matches the doctor pill); 'intakt' stays the state headline. EN was already consistent. - Workspace switcher: contrast bump for the sealed-area 'stopped' label. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
6152c80c84
commit
ae8fdcc762
12 changed files with 69 additions and 9 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -6,6 +6,20 @@ lockstep.
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed (usertest low-findings tail)
|
||||||
|
|
||||||
|
- **Honest hero badge.** The store hero wears "TODAY"/"HEUTE" only
|
||||||
|
for an operator-accepted story; the compiled-in fallback deck has
|
||||||
|
no date binding and now says "FEATURED"/"EMPFOHLEN".
|
||||||
|
- **Audit filter chips in UI type.** The Alle/Flow/Schritt/Modul
|
||||||
|
chips drop the monospace styling — mono stays reserved for paths
|
||||||
|
and identifiers.
|
||||||
|
- **One verb for the chain check (DE).** "Hash-Kette geprüft" is the
|
||||||
|
confirmation everywhere (audit header, doctor pill); "intakt"
|
||||||
|
remains the state headline. English was already consistent.
|
||||||
|
- **Readable sealed-area status.** The "stopped" label in the
|
||||||
|
workspace switcher gets a modest contrast bump.
|
||||||
|
|
||||||
### Changed (usertest panel 2026-07-17, 14 personas — all confirmed findings)
|
### Changed (usertest panel 2026-07-17, 14 personas — all confirmed findings)
|
||||||
|
|
||||||
- **Install trust gate.** One confirmation dialog before every module
|
- **Install trust gate.** One confirmation dialog before every module
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
/// Studio's own build version. Bump on every UI release so the
|
/// Studio's own build version. Bump on every UI release so the
|
||||||
/// running app self-identifies.
|
/// running app self-identifies.
|
||||||
const String kStudioVersion = '0.72.0';
|
const String kStudioVersion = '0.72.1';
|
||||||
|
|
||||||
const String kProductName = 'Ch∆In Studio';
|
const String kProductName = 'Ch∆In Studio';
|
||||||
const String kVendorName = 'Flemming.AI (F∆I)';
|
const String kVendorName = 'Flemming.AI (F∆I)';
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,7 @@
|
||||||
"storesManagerNotAvailableHint": "Der Anbieter hat diesen Store-Index noch nicht veröffentlicht. Sobald er das tut, ist er installierbar.",
|
"storesManagerNotAvailableHint": "Der Anbieter hat diesen Store-Index noch nicht veröffentlicht. Sobald er das tut, ist er installierbar.",
|
||||||
"storesSuggestedReclaimDesc": "Recl∆Im — Module für Rechts- und Pflichten-Reform-Mapping",
|
"storesSuggestedReclaimDesc": "Recl∆Im — Module für Rechts- und Pflichten-Reform-Mapping",
|
||||||
"storeTodayBadge": "HEUTE",
|
"storeTodayBadge": "HEUTE",
|
||||||
|
"storeFeaturedBadge": "EMPFOHLEN",
|
||||||
"storeTodayDeck": "Von der Ch∆In-Redaktion",
|
"storeTodayDeck": "Von der Ch∆In-Redaktion",
|
||||||
"storeTodayDismissTooltip": "Für diese Sitzung ausblenden",
|
"storeTodayDismissTooltip": "Für diese Sitzung ausblenden",
|
||||||
"storePillInstalled": "installiert",
|
"storePillInstalled": "installiert",
|
||||||
|
|
@ -629,7 +630,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auditHashChainVerified": "Hash-Kette verifiziert",
|
"auditHashChainVerified": "Hash-Kette geprüft",
|
||||||
"auditDetailHeader": "DETAIL",
|
"auditDetailHeader": "DETAIL",
|
||||||
"auditAskingFull": "Frage an die konfigurierte System-AI läuft…",
|
"auditAskingFull": "Frage an die konfigurierte System-AI läuft…",
|
||||||
"auditFixLabel": "FIX",
|
"auditFixLabel": "FIX",
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,7 @@
|
||||||
"storesManagerNotAvailableHint": "The provider hasn't published this store index yet. It will become installable once they do.",
|
"storesManagerNotAvailableHint": "The provider hasn't published this store index yet. It will become installable once they do.",
|
||||||
"storesSuggestedReclaimDesc": "Recl∆Im — modules for law & duty reform-mapping",
|
"storesSuggestedReclaimDesc": "Recl∆Im — modules for law & duty reform-mapping",
|
||||||
"storeTodayBadge": "TODAY",
|
"storeTodayBadge": "TODAY",
|
||||||
|
"storeFeaturedBadge": "FEATURED",
|
||||||
"storeTodayDeck": "From the Ch∆In editor",
|
"storeTodayDeck": "From the Ch∆In editor",
|
||||||
"storeTodayDismissTooltip": "Hide for this session",
|
"storeTodayDismissTooltip": "Hide for this session",
|
||||||
"storePillInstalled": "installed",
|
"storePillInstalled": "installed",
|
||||||
|
|
|
||||||
|
|
@ -1766,6 +1766,12 @@ abstract class AppLocalizations {
|
||||||
/// **'TODAY'**
|
/// **'TODAY'**
|
||||||
String get storeTodayBadge;
|
String get storeTodayBadge;
|
||||||
|
|
||||||
|
/// No description provided for @storeFeaturedBadge.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'FEATURED'**
|
||||||
|
String get storeFeaturedBadge;
|
||||||
|
|
||||||
/// No description provided for @storeTodayDeck.
|
/// No description provided for @storeTodayDeck.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
|
||||||
|
|
@ -957,6 +957,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get storeTodayBadge => 'HEUTE';
|
String get storeTodayBadge => 'HEUTE';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get storeFeaturedBadge => 'EMPFOHLEN';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storeTodayDeck => 'Von der Ch∆In-Redaktion';
|
String get storeTodayDeck => 'Von der Ch∆In-Redaktion';
|
||||||
|
|
||||||
|
|
@ -1275,7 +1278,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get auditHashChainVerified => 'Hash-Kette verifiziert';
|
String get auditHashChainVerified => 'Hash-Kette geprüft';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get auditDetailHeader => 'DETAIL';
|
String get auditDetailHeader => 'DETAIL';
|
||||||
|
|
|
||||||
|
|
@ -974,6 +974,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get storeTodayBadge => 'TODAY';
|
String get storeTodayBadge => 'TODAY';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get storeFeaturedBadge => 'FEATURED';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storeTodayDeck => 'From the Ch∆In editor';
|
String get storeTodayDeck => 'From the Ch∆In editor';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -620,9 +620,11 @@ class _ChipButtonState extends State<_ChipButton> {
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.label,
|
widget.label,
|
||||||
style: ChainTheme.mono(
|
// UI label, not a value — mono is reserved for
|
||||||
size: 11,
|
// paths/identifiers (usertest finding: the chips
|
||||||
weight: widget.selected ? FontWeight.w500 : FontWeight.w400,
|
// read as foreign bodies in the header).
|
||||||
|
style: theme.textTheme.labelMedium?.copyWith(
|
||||||
|
fontWeight: widget.selected ? FontWeight.w600 : FontWeight.w400,
|
||||||
color: fg,
|
color: fg,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -3207,6 +3207,13 @@ class _ScreenshotPlaceholder extends StatelessWidget {
|
||||||
/// not abstract platform philosophy. Architecture education
|
/// not abstract platform philosophy. Architecture education
|
||||||
/// (sandbox model, hash-chained audit, air-gap posture) lives
|
/// (sandbox model, hash-chained audit, air-gap posture) lives
|
||||||
/// on the dedicated Welcome page, not in the store carousel.
|
/// on the dedicated Welcome page, not in the store carousel.
|
||||||
|
/// Whether the hero deck may wear the date-bound "TODAY" badge.
|
||||||
|
/// Only a single, operator-accepted story qualifies — the
|
||||||
|
/// compiled-in fallback deck rotates unchanged across sessions
|
||||||
|
/// and must not claim freshness ("FEATURED" instead). Top-level
|
||||||
|
/// so the policy stays unit-testable.
|
||||||
|
bool storeDeckIsAcceptedToday(int totalCount) => totalCount == 1;
|
||||||
|
|
||||||
const List<TodayStoryData> _kFallbackTodayStories = <TodayStoryData>[
|
const List<TodayStoryData> _kFallbackTodayStories = <TodayStoryData>[
|
||||||
TodayStoryData(
|
TodayStoryData(
|
||||||
badgeEn: 'MCP',
|
badgeEn: 'MCP',
|
||||||
|
|
@ -3433,7 +3440,12 @@ class _StoreTodayHero extends StatelessWidget {
|
||||||
runSpacing: 4,
|
runSpacing: 4,
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
ChainPill(label: l.storeTodayBadge, tone: ChainPillTone.accent),
|
ChainPill(
|
||||||
|
label: storeDeckIsAcceptedToday(totalCount)
|
||||||
|
? l.storeTodayBadge
|
||||||
|
: l.storeFeaturedBadge,
|
||||||
|
tone: ChainPillTone.accent,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
badge,
|
badge,
|
||||||
style: theme.textTheme.labelSmall?.copyWith(
|
style: theme.textTheme.labelSmall?.copyWith(
|
||||||
|
|
|
||||||
|
|
@ -149,9 +149,12 @@ class ChainWorkspaceSwitcher extends StatelessWidget {
|
||||||
? l.workspaceSealedRunning
|
? l.workspaceSealedRunning
|
||||||
: l.workspaceSealedStopped,
|
: l.workspaceSealedStopped,
|
||||||
style: theme.textTheme.labelSmall?.copyWith(
|
style: theme.textTheme.labelSmall?.copyWith(
|
||||||
|
// Secondary but readable — onSurfaceVariant
|
||||||
|
// fell below comfortable contrast at this
|
||||||
|
// size (usertest finding).
|
||||||
color: a.running
|
color: a.running
|
||||||
? ChainColors.success
|
? ChainColors.success
|
||||||
: theme.colorScheme.onSurfaceVariant,
|
: theme.colorScheme.onSurface.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
name: chain_studio
|
name: chain_studio
|
||||||
description: "Ch∆In Studio — desktop GUI for the Ch∆In hub"
|
description: "Ch∆In Studio — desktop GUI for the Ch∆In hub"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.72.0
|
version: 0.72.1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.11.0-200.1.beta
|
sdk: ^3.11.0-200.1.beta
|
||||||
|
|
|
||||||
15
test/store_today_badge_test.dart
Normal file
15
test/store_today_badge_test.dart
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import 'package:chain_studio/pages/store.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('store hero badge policy', () {
|
||||||
|
test('a single operator-accepted story may claim TODAY', () {
|
||||||
|
expect(storeDeckIsAcceptedToday(1), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the rotating fallback deck must not claim TODAY', () {
|
||||||
|
expect(storeDeckIsAcceptedToday(2), isFalse);
|
||||||
|
expect(storeDeckIsAcceptedToday(8), isFalse);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue