Skip to content

frontend Mod Controller Compatibility Info and Required Disclosures system#255

Merged
Vilsol merged 52 commits into
stagingfrom
disclosures
May 25, 2026
Merged

frontend Mod Controller Compatibility Info and Required Disclosures system#255
Vilsol merged 52 commits into
stagingfrom
disclosures

Conversation

@budak7273
Copy link
Copy Markdown
Member

@budak7273 budak7273 commented May 24, 2026

CI will fail until backed is merged into staging

Frontend side of satisfactorymodding/smr-api#138

  • Updated documentation
  • Comment warning mod authors that if they delete a version, the semver can't be reused
  • More strings translatable
  • Additional dotted underline tooltips
  • Log when user's local SMM couldn't be reached (should make browser console logs less confusing to curious users)

  • Controller compatibility info
    • Can be set back to Unknown unlike EA/EXP compatibility info
  • Unused ToggleNetworkUse removed
  • Required Disclosures system (this+Warning when uploading a mod version that depends on a min SML version older than the latest #132)
    • Network Activity Transparency is now a dedicated field instead of to be put in mod descriptions
    • New AI usage disclosure dedicated field
    • New Mod flow intentionally does not allow specifying these disclosures, they can be filled out later once the author has actual answers to them
    • New mod version uploads are blocked until disclosures have been provided
    • Existing mods have no disclosure set

@vercel
Copy link
Copy Markdown

vercel Bot commented May 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
smr-frontend Error Error May 25, 2026 7:07pm

budak7273 and others added 26 commits May 24, 2026 03:16
requires smr-api:1e106ce740e6695cedc04a6c7c2efe7df7521fe1
was breaking Edit Mod
need to decide what to do about the old api-only bool
…s and logic. fix old value lost when switching between dropdown options
Copy link
Copy Markdown
Member

@Vilsol Vilsol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(claude-authored)

Thanks for tackling this — most of the PR reads well. Below are findings from a structured review (reuse / quality / efficiency lenses). Ordered roughly by severity.

🐛 Bugs / correctness

1. updateMod is missing from the graphcache updates.Mutation blocksrc/lib/core/graphql.ts

The block lists deleteMod/approveMod/denyMod/updateVersion etc., but not updateMod. The gap is pre-existing on staging, but this PR meaningfully widens its impact by making network_use_disclosure and ai_use_disclosure editable: after saving them in ModForm, the urql cache will keep serving the old Mod entity until a full reload. Suggest adding alongside the existing entries:

updateMod(_result, args, cache) {
  cache.invalidate({ __typename: 'Mod', id: args.modId as string });
},

2. iconText is computed once at component init, not reactivesrc/lib/components/mods/compatibility/controller/ControllerCompatibilityIcon.svelte#L6-L9

let iconText = 'videogame_asset';
if (compatibility?.state === ControllerCompatibilityState.Unsupported) {
  iconText = 'videogame_asset_off';
}

The class bindings below are reactive, but iconText runs once. If compatibility changes during edit (and that's exactly the new use case — bind: on the state in ControllerCompatibilityEdit), the icon won't switch from videogame_asset to videogame_asset_off. The existing branch/CompatibilityIcon.svelte depends only on the EXP prop which is fixed per-instance, so this pattern was safe there but isn't here.

$: iconText = compatibility?.state === ControllerCompatibilityState.Unsupported
  ? 'videogame_asset_off'
  : 'videogame_asset';

3. Debug console.log shipping to production

  • src/lib/components/mods/ModForm.svelteconsole.log('DEBUG: ModForm Errors', e). The comment says "Intentionally kept in case we run into some weird edge cases," but it fires on every validation error for every user. Either drop it or gate it behind import.meta.env.DEV.
  • src/lib/utils/uplugin.ts#L60window.console.log('Invalid json:', e). Drop the window. prefix (it's a no-op in browsers and breaks under SSR if this ever executes server-side), and consider console.error so it shows up in error filters.

✂️ Reuse / duplication

4. ControllerCompatibility* components are near-duplicates of branch/Compatibility*

  • ControllerCompatibilityStateTextbranch/CompatibilityStateText (state → text → classForState<p class="… mod-state">), differing only in enum + i18n key prefix.
  • ControllerCompatibilityEditbranch/CompatibilityEdit (label/select/description/textarea), differing only in enum + description fn + label.
  • The new mod-controller-state-* classes in _global.postcss have identical colors to the existing mod-state-* rules — they could be deleted and the existing classes reused via a small state-equivalence mapping (the class bindings in ControllerCompatibilityIcon already imply that mapping: Implicit/Supported → works, Partial → damaged, Unsupported → broken, Untested/null → unknown).

A single parameterized <CompatibilityStateText prefix={…} state={…}/> + <CompatibilityEdit states={…} descriptionFn={…} label={…}/> covers both branch and controller and would remove most of the controller folder.

🎨 Code quality

5. Leaky bind:dropdownChoiceForValidationModNetworkDisclosureEdit.svelte exports an internal dropdown state purely so ModForm can validate. The child's UI state shouldn't have to escape — either keep validation inside the child, expose a typed event, or compute the validation input from disclosure itself.

6. IIFE inside the Svelte templateModForm.svelte ~line 235-252 wraps the error-message branching in (() => { ... })(). Lift it into a small helper (formatDisclosureError(message)) or a $: derived var — easier to read and unit-test.

7. Stringly-typed mapsrc/lib/utils/compatibility-descriptions.ts

export const compatibilityStateDescriptions: {
  // TODO switch to Record, but Unknown isn't a valid db type
  [key: string]: string;
} = { Works: , Damaged: , Broken: , Unknown:  };

The TODO is honest, but controllerCompatibilityStateDescriptions two lines below uses Record<ControllerCompatibilityState, string> cleanly. For the branch one you can use Record<CompatibilityState, string> & { Unknown: string } (or a small string-union of CompatibilityState | 'Unknown') and keep type safety on the real enum values.

8. <!-- @ts-ignore -->ModAiDisclosureEdit.svelte line 45. The state in {#each dropdown_options as state} is AiUseDisclosureType and optionTranslationKeys[state] returns a known string from a Record, so this should be resolvable by typing the array more precisely (it's already as Array<AiUseDisclosureType> — confirm Tolgee's T component is happy with the resulting string, or extract to a typed const).

9. Generic aria-label on the new controller tableCompatibilityGrid.svelte. The existing table was correctly relabeled to "Branch Compatibility Information" in this PR, but the new table inherited the now-misleading "Compatibility Information" label. Suggest "Controller Compatibility Information".

10. Dangling <br />ControllerCompatibilityEdit.svelte line ~28. The PR explicitly removes the same <br /> from branch/CompatibilityEdit.svelte, but the new controller copy was written with the old style. Margin-bottom on the label/select handles spacing already.

Nice-to-haves (lower confidence)

  • The controller table is rendered as a second <table> element. It currently has only one column, so unifying it with the branch table isn't trivial; if you ever add a second controller variant it might be worth revisiting.
  • errors.subscribe(...) in ModForm.svelte doesn't capture/cleanup the unsubscribe handle. Felte's store is component-scoped so it's likely fine, but using $errors reactively would auto-cleanup and avoid the question.

Happy to discuss / push fixes if useful.

@budak7273 budak7273 changed the title Mod Controller Compatibility Info and Required Disclosures system frontend Mod Controller Compatibility Info and Required Disclosures system May 24, 2026
@budak7273
Copy link
Copy Markdown
Member Author

Addressed 1 (actual behavior untested), 2, 5 (by adding comment), 7, 8 (no longer needed with refactor), 9, 10

Skipped 3

The purpose is for it to be there on production so we don't need to push special builds that let people see errors in the future when reporting them

Skipped 4

The refactor is not worth the effort right now, and using the prefix system makes it harder to find i18n key usage in the code

Skipped 6

Yes, the error display is ugly, but better it be right where the error gets displayed when nothing else in the file cares about it

@Vilsol Vilsol merged commit 46ec688 into staging May 25, 2026
14 of 22 checks passed
@github-project-automation github-project-automation Bot moved this from 🆕 New to ✅ Completed in Mod Repository (SMR) May 25, 2026
@Vilsol Vilsol deleted the disclosures branch May 25, 2026 21:10
@Vilsol
Copy link
Copy Markdown
Member

Vilsol commented May 25, 2026

@claude review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Completed

Development

Successfully merging this pull request may close these issues.

3 participants