A to-do app that doesn't believe you — an enforcement layer rather than a
neutral ledger.
Architecture ported from Autoreceptives/Frontend/Receptive: stacked MVVM with
the mandatory 4-file screen pattern, one ParentViewModel owning the loading /
network / error overlays and the handleError decision tree, one AppDataManager
gateway, dio comms carrying the three identity headers, secure storage with
random-suffixed keys, and a single-chokepoint Navigator. Package root and Dart
package name are both Grounded; org is nya.
The enforcement engine, one unit per formula in utils/:
- DebtEngine w(class) x severity(d) x decay(t), sublinear severity so old
misses cannot swamp the score; abandonment 2x with 30-day
decay immunity; late complete retains 30%
- StandingEngine Good -> Warned -> Grounded -> Lockdown, derived not set;
Grounded replaces home with the overdue queue
- CapacityEngine blocks over-scheduling against p50 of historically completed
minutes, with a learned per-category estimation multiplier
- IntegrityEngine session integrity, weekly volume, plyometric contact ceiling
and enforced recovery gaps
- ExcuseAnalyser on-device excuse clustering plus the confrontation copy
- GuardrailEngine distress detection and rationed amnesty
- ToneEngine all enforcement copy, so the tone cap lives in one place
CommitmentEvent is append-only and is the source of truth rather than the
status field, which is what makes honest history and excuse analysis possible.
Goals contain commitments via parentId, and a task can be run from a
full-screen runner that derives elapsed time from wall-clock so screen-off
cannot lose time. Backgrounding pauses the clock and is counted. The runner is
mirrored into an ongoing notification, with alarm-class full-screen intents
reserved for non-negotiables.
Design language, fonts, icon and native splash are in place; Mason bricks are
retargeted to this project and verified end-to-end.
flutter analyze lib/ reports no errors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mfu2gQLSFN21YRBcU2NrTt
116 lines
3.3 KiB
HTML
116 lines
3.3 KiB
HTML
<!DOCTYPE html><html><head>
|
|
<!--
|
|
If you are serving your web app in a path other than the root, change the
|
|
href value below to reflect the base path you are serving from.
|
|
|
|
The path provided below has to start and end with a slash "/" in order for
|
|
it to work correctly.
|
|
|
|
For more details:
|
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
|
|
|
This is a placeholder for base href that will be replaced by the value of
|
|
the `--base-href` argument provided to `flutter build`.
|
|
-->
|
|
<base href="$FLUTTER_BASE_HREF">
|
|
|
|
<meta charset="UTF-8">
|
|
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
|
<meta name="description" content="A new Flutter project.">
|
|
|
|
<!-- iOS meta tags & icons -->
|
|
<meta name="mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
|
<meta name="apple-mobile-web-app-title" content="grounded">
|
|
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
|
|
|
<!-- Favicon -->
|
|
<link rel="icon" type="image/png" href="favicon.png">
|
|
|
|
<title>grounded</title>
|
|
<link rel="manifest" href="manifest.json">
|
|
<style id="splash-screen-style">
|
|
html {
|
|
height: 100%
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
min-height: 100%;
|
|
background-color: #141414;
|
|
background-size: 100% 100%;
|
|
}
|
|
|
|
.center {
|
|
margin: 0;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
-ms-transform: translate(-50%, -50%);
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
.contain {
|
|
display:block;
|
|
width:100%; height:100%;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.stretch {
|
|
display:block;
|
|
width:100%; height:100%;
|
|
}
|
|
|
|
.cover {
|
|
display:block;
|
|
width:100%; height:100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.bottom {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
-ms-transform: translate(-50%, 0);
|
|
transform: translate(-50%, 0);
|
|
}
|
|
|
|
.bottomLeft {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
}
|
|
|
|
.bottomRight {
|
|
position: absolute;
|
|
bottom: 0;
|
|
right: 0;
|
|
}
|
|
</style>
|
|
<script id="splash-screen-script">
|
|
function removeSplashFromWeb() {
|
|
document.getElementById("splash")?.remove();
|
|
document.getElementById("splash-branding")?.remove();
|
|
document.body.style.background = "transparent";
|
|
}
|
|
</script>
|
|
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
|
</head>
|
|
<body>
|
|
<picture id="splash">
|
|
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)">
|
|
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
|
|
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
|
|
</picture>
|
|
<!--
|
|
You can customize the "flutter_bootstrap.js" script.
|
|
This is useful to provide a custom configuration to the Flutter loader
|
|
or to give the user feedback during the initialization process.
|
|
|
|
For more details:
|
|
* https://docs.flutter.dev/platform-integration/web/initialization
|
|
-->
|
|
<script src="flutter_bootstrap.js" async=""></script>
|
|
|
|
|
|
</body></html> |