fission-command-core 0.8.0

Shared project, manifest, target, and diagnostic model for the fission command
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# Fission App Guidelines

These instructions apply when building or reviewing a Fission-based app in this
tree.

## Source-Grounded Work

- Start from the real app entrypoint, then trace into screens, reusable widgets,
  and lower-level render behavior before changing UI code.
- For UI reviews, group findings by visible region. Do not stop at a screenshot
  description; trace the component and widget code that creates each issue.
- When a visual problem looks small, keep looking for related spacing,
  alignment, overflow, typography, state, and target-specific issues before
  reporting completion.
- Keep edits scoped to the component, widget, route, shell, or target behavior
  being changed. Avoid broad cleanup unless it is required for the task.

## Product and UX Principles

- Design for users with limited attention, uncertainty, effort, and motivation.
  Strong product UX is about reducing friction and helping users complete their
  own goals, not only visual polish.
- Use smart defaults when the likely answer is predictable. Defaults should be
  editable, visible, and chosen for the user's benefit rather than to quietly
  advantage the business.
- Give users a truthful sense of progress. Credit real completed work, show what
  remains, and avoid fake progress percentages.
- Demonstrate useful value before asking for account creation, payment, or other
  commitment. The ask should preserve, extend, or personalize something the user
  already understands.
- Move creation before administration where possible: choose, create, see a
  result, then save. Do not manufacture busywork or tedious sunk costs.
- Make genuine consequences of inaction visible without inventing urgency,
  scarcity, guilt, or risk. Prefer neutral labels such as "Continue without
  backup" over shaming labels.
- Provide pricing and plan context with fair comparisons: total cost, cost per
  active user, included usage, time saved, or risk covered. Do not hide the true
  total behind a smaller unit.

## Product-Led Growth and Shareability

- Prefer features that can demonstrate themselves quickly. A good product moment
  should be understandable in a few seconds without a long explanation.
- Build at least one memorable, repeatable product action that can appear inside
  natural user stories, screenshots, short demos, or creator content.
- Let the product itself be the creative: show the problem, the action, and the
  satisfying result directly in the UI.
- Create curiosity ethically. It is fine for a feature to invite "what app is
  that?" moments; it is not fine to mislead users or hide essential information.
- Design shareable outputs, saved states, exports, or previews when they serve
  the user's goal. Do not add viral mechanics that interrupt the core task.

## Visual Design and Progressive Disclosure

- Use a modern, calm visual design with clear hierarchy, strong typography, and
  purposeful spacing.
- Use progressive disclosure. Show the next meaningful action first, then reveal
  advanced options when the user needs them.
- Build responsive UIs by default. At minimum, design and verify mobile and
  desktop layouts rather than assuming one fixed window size.
- Boxes are for grouping related things. If everything is inside a box, boxes
  stop communicating structure and make the interface feel noisy.
- Prefer specific, outcome-oriented actions over generic labels. For example,
  "View 12 results" is usually better than "Search" once the result count is
  known.
- Empty states should explain what is missing, why it matters, and the smallest
  next step the user can take.

## Widget Structure

- Keep Rust source files below 2,000 lines. Refactor into focused modules before
  a file grows past that limit.
- Organize Rust code using production module boundaries: entrypoints, app state,
  routes, screens, reusable widgets, effects/jobs/services, design-system code,
  and test helpers should not collapse into one large file.
- Prefer the Fission CLI for project, target, packaging, and shell scaffolding
  instead of hand-rolling platform boilerplate.
- For multi-platform apps, put the app state, reducers, routes, widgets,
  services, design system, and i18n setup in a shared core. Keep each
  feature-gated shell entrypoint thin: it should configure the shell and run the
  shared core rather than duplicating app behavior per target.
- Prefer one reusable widget per file when introducing app UI.
- Model widgets as concrete structs and implement `From<YourWidget> for Widget`.
- Add semantics to every interactive widget and to semantic regions where they
  improve navigation, testing, or accessibility.
- Use `#[fission_component]` for components that own retained local widget
  state. Do not model retained UI state as ordinary mutable struct fields.
- Keep screen modules focused on app state, routing, effects, and composition.
  Move reusable presentation pieces into widget modules.
- Avoid screen-level helper functions that build large `Widget` trees. If a UI
  fragment is meaningful or reused, make it a named widget struct instead.
- Small private helper functions are acceptable for narrow formatting,
  conversion, or leaf construction, but they should not hide reusable component
  boundaries. Avoid functions that return `Widget` when a named widget struct
  would represent the boundary better. Fission is a retained UI; building
  meaningful UI fragments through anonymous functions can hide identity and
  weaken optimizations.

Local-state component shape:

```rust
use fission::prelude::*;

#[fission_component]
pub struct DisclosureSection {
    pub title: String,

    #[local_state(default = false)]
    open: bool,
}

#[fission_reducer(ToggleOpen)]
fn on_toggle_open(open: &mut bool) {
    *open = !*open;
}

impl From<DisclosureSection> for Widget {
    fn from(section: DisclosureSection) -> Widget {
        let (ctx, _) = fission::build::current::<()>();
        let open = section.open();
        let toggle = ctx.bind_local(ToggleOpen, open.clone(), reduce!(on_toggle_open));

        Column {
            gap: Some(8.0),
            children: widgets![
                Button {
                    on_press: Some(toggle),
                    child: Some(Text::new(section.title).into()),
                    ..Default::default()
                },
                if open.get() {
                    Text::new("The details are visible.").into()
                } else {
                    Text::new("The details are hidden.").into()
                },
            ],
            ..Default::default()
        }
        .into()
    }
}
```

Use `fission::build::current::<()>()` only when the component is intentionally
state-agnostic and does not read app state. Use the concrete app state type when
the component reads `GlobalState`, reads environment values tied to that app, or
binds reducers that update app state.

## State, Reducers, Routing and Runtime Data

- Choose the smallest state bucket that matches the lifetime and ownership of
  the data.
- Use `GlobalState` for durable app truth: product data, routing, sync,
  persistence, user preferences, shared filters, and values read by distant
  screens.
- Use `#[local_state]` for retained UI memory owned by one widget identity:
  open/closed flags, isolated draft text, local selected tabs, hoverless
  interaction state, or a counter-like local value.
- Use reducers on `GlobalState` when an action updates app data. Use
  `ctx.bind_local(...)` when an action updates one local-state field.
- Use reducers for explicit state transitions. Match the dispatched action to the
  reducer registration used by the widget.
- Do not store `BuildCtxHandle` or `ViewHandle` in structs, reducers, services,
  async tasks, statics, or other long-lived places. They are build-scope handles.
- Do not mutate `GlobalState` during component conversion. Dispatch actions and
  update state in reducers.
- Do not start network requests, file writes, or host operations during component
  conversion. Request work through effects, jobs, services, capabilities, or
  resources.
- For local-state components rendered from dynamic, reorderable, insertable, or
  filterable data, assign stable widget identities with
  `.id(WidgetId::explicit(...))`. Use a durable data id, not the list index.
- Do not pad production UI with fixture, mock, or demo data. Production paths
  should render persisted data, an explicit empty state, or a clear error state.
- Keep fixture parsing, fixture environment variables, and test-only data inside
  test infrastructure.
- When adding routed screens, verify the frontend route shape and any backend or
  deep-link route shape intentionally match.
- Use Fission's native Router and RouterParams (https://fission.rs/reference/widgets/router/). This ensures routing behaves consistently across ios, android, windows, linux, mac, terminal, static and server rendered sites

## Design System

Fission supports Adobe's Design System Package (DSP). Use DSP JSON as the design
source, generate the Rust design-system type at build time, and read generated
tokens from the active Fission environment at runtime.

- Prefer a generated Fission design system and its tokens over hard-coded sizes,
  colors, typography, spacing, radii, shadows, or component properties.
- Hard-coded visual values are acceptable only for narrow one-off geometry that
  is not part of the app's design language.

Use these Fission defaults as the starting point for a custom design system:

- Copy `dsp.json` from
  <https://github.com/fission-ui/fission/blob/main/crates/core/fission-theme/design/default/dsp.json>
  into the app, for example at `design/dsp.json`.
- Copy `tokens.json` from
  <https://github.com/fission-ui/fission/blob/main/crates/core/fission-theme/design/default/tokens.json>
  into the app, for example at `design/tokens.json`.
- Customize the checked-in copies. Do not parse the upstream default files at
  runtime.
- Follow the official design-system example:
  <https://github.com/fission-ui/fission/tree/main/examples/todo-design-system>.

Add the code generator as a build dependency. Use the dependency source that
matches this repository, and keep it version-aligned with the Fission runtime.
Do not leave a path dependency in place unless that path exists in this checkout.

Outside the Fission source workspace, prefer the published crate pinned to the same Fission revision used by the app:

```toml
[build-dependencies]
fission-design-system-codegen = { version = "0.8.0" }
```

Generate a typed design system from the copied DSP file in `build.rs`:

```rust
use std::path::PathBuf;

fn main() {
    println!("cargo:rerun-if-changed=design/dsp.json");
    println!("cargo:rerun-if-changed=design/tokens.json");

    let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
    let dsp_path = manifest_dir.join("design/dsp.json");

    fission_design_system_codegen::generate(fission_design_system_codegen::Config {
        dsp_path,
        out_file: "app_design_system.rs".into(),
        type_name: "AppDesignSystem".into(),
        crate_path: "fission::theme".into(),
    })
    .expect("failed to generate AppDesignSystem from design/dsp.json");
}
```

Include the generated type and install it on app startup:

```rust
use fission::prelude::*;

include!(concat!(env!("OUT_DIR"), "/app_design_system.rs"));

#[derive(Debug, Clone, PartialEq, Eq)]
struct AppState {
    theme_mode: DesignMode,
}

impl Default for AppState {
    fn default() -> Self {
        Self {
            theme_mode: DesignMode::Light,
        }
    }
}

impl GlobalState for AppState {}

fn main() -> anyhow::Result<()> {
    DesktopApp::<AppState, _>::new(App)
        .with_design_system::<AppDesignSystem>(DesignMode::Light)
        .with_sync_env(|state: &AppState, env: &mut Env| {
            env.theme = AppDesignSystem::theme(state.theme_mode);
        })
        .run()
}
```

Inside widgets, read colors, spacing, typography, radii, and component styling
from `view.env().theme`:

```rust
impl From<App> for Widget {
    fn from(_app: App) -> Widget {
        let (_ctx, view) = fission::build::current::<AppState>();
        let tokens = &view.env().theme.tokens;

        Container::new(Text::new("Dashboard"))
            .bg(tokens.colors.background)
            .padding_all(tokens.spacing.xl)
            .into()
    }
}
```

Do not guess token names. Inspect the generated Rust output or the copied DSP
and tokens files before wiring new design values.

## Theme and Locale Synchronization

Theme and locale are app-wide presentation inputs. Keep user-controlled choices
in `GlobalState`, then mirror them into `Env` with `.with_sync_env(...)`.

```rust
DesktopApp::<AppState, _>::new(App)
    .with_env(create_env()?)
    .with_design_system::<AppDesignSystem>(DesignMode::Light)
    .with_sync_env(|state: &AppState, env: &mut Env| {
        env.locale = state.locale.clone();
        env.theme = AppDesignSystem::theme(state.theme_mode);
    })
    .run()
```

Use `.with_sync_env(...)` only for app-wide presentation inputs such as theme,
locale, and similar environment values. Do not use it for network loading, file
writes, job startup, or ordinary domain data.

## Internationalization

Follow the Fission theming and i18n guide:
<https://github.com/fission-ui/fission/blob/main/documentation/content/docs/guides/theming-and-i18n.mdx>.

- Include an i18n setup from the start with at least two languages. Always
  include English. If the system default language is not English, add that
  language too; if it is English, add Spanish as the second language.
- Do not hard-code user-facing text inside reusable widgets.
- Use stable translation keys based on meaning, not on the current layout.
- Keep translations in checked-in files and embed them with `include_str!` unless
  the app has a stronger reason to load translations dynamically.
- Parse translation files into `HashMap<String, String>`, wrap each map in a
  `TranslationBundle`, and add it to `env.i18n`.
- If the user can change language, store the current locale in `GlobalState` and
  mirror it to `env.locale` in `.with_sync_env(...)`.
- Test long translated strings, not only short English labels.

Example translation files:

```yaml
# i18n/en-US.yaml
settings.title: "Settings"
settings.theme.light: "Light"
settings.theme.dark: "Dark"
settings.language.english: "English"
settings.language.spanish: "Spanish"
```

```yaml
# i18n/es-ES.yaml
settings.title: "Configuracion"
settings.theme.light: "Claro"
settings.theme.dark: "Oscuro"
settings.language.english: "Ingles"
settings.language.spanish: "Espanol"
```

Add a YAML parser if the app uses YAML translation files:

```toml
[dependencies]
serde_yaml = "0.9"
```

Load bundles into `Env`:

```rust
use std::collections::HashMap;

use fission::i18n::{Locale, TranslationBundle};
use fission::prelude::*;

fn load_bundle(locale: &str, raw_yaml: &str) -> anyhow::Result<TranslationBundle> {
    let messages: HashMap<String, String> = serde_yaml::from_str(raw_yaml)?;

    Ok(TranslationBundle {
        locale: Locale::from(locale),
        messages,
    })
}

fn create_env() -> anyhow::Result<Env> {
    let mut env = Env::default();

    env.i18n
        .add_bundle(load_bundle("en-US", include_str!("../i18n/en-US.yaml"))?);
    env.i18n
        .add_bundle(load_bundle("es-ES", include_str!("../i18n/es-ES.yaml"))?);

    Ok(env)
}
```

Render translated text with keys instead of literals:

```rust
Text::new(TextContent::Key("settings.title".into()))
```

For SSR and Static site targets, seed `Env` with the same bundles. Resolve the
locale from request or build context in the shell instead of making individual
widgets guess.

## Source References

- Design-system example directory:
  <https://github.com/fission-ui/fission/tree/main/examples/todo-design-system>
- Design-system example `build.rs`:
  <https://github.com/fission-ui/fission/blob/main/examples/todo-design-system/build.rs>
- Design-system example `main.rs`:
  <https://github.com/fission-ui/fission/blob/main/examples/todo-design-system/src/main.rs>
- Design-system guide:
  <https://github.com/fission-ui/fission/blob/main/documentation/content/docs/guides/design-system.mdx>
- Theming and i18n guide:
  <https://github.com/fission-ui/fission/blob/main/documentation/content/docs/guides/theming-and-i18n.mdx>
- State, handles, and providers guide:
  <https://github.com/fission-ui/fission/blob/main/documentation/content/docs/guides/state-handles-and-providers.mdx>
- Default DSP package:
  <https://github.com/fission-ui/fission/blob/main/crates/core/fission-theme/design/default/dsp.json>
- Default token file:
  <https://github.com/fission-ui/fission/blob/main/crates/core/fission-theme/design/default/tokens.json>

## Validation

- Run formatting before handing off code changes.
- Run the narrowest compile or test command that exercises the changed app,
  widget crate, route, or target shell.
- Use the Fission LiveTest API for end-to-end UI tests:
  <https://fission.rs/docs/cookbook/write-a-live-ui-test/>.
- For visual UI work, use a client such as `curl` to send `/cmd` requests to the
  LiveTest API when needed, drive the app like a user, take screenshots, and
  inspect those screenshots before calling the UI production-ready.
- For UI changes, verify a real rendered target when possible, and check mobile
  and desktop layouts when the screen is responsive.
- If docs or configuration target names are changed, keep terminology consistent
  with Fission's public target names: `macOS`, `Windows`, `Linux`, `Web`,
  `Android`, `iOS`, `Terminal`, `Static site`, and `SSR`.