Skip to content

refactor: moved wait and analysis stores to pinia - #1918

Open
tworkman08 wants to merge 16 commits into
fluidd-core:developfrom
tworkman08:pinia-refactor
Open

refactor: moved wait and analysis stores to pinia#1918
tworkman08 wants to merge 16 commits into
fluidd-core:developfrom
tworkman08:pinia-refactor

Conversation

@tworkman08

@tworkman08 tworkman08 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

To support potential future work to move to Vue 3, Vuex needs to be replaced with Pinia. I started with a couple of easy stores (analysis and wait). Pinia convention recommends a flatter file structure than was needed with Vuex. However, I still kept the stores separated by folder to facilitate breaking them out into actions and getters if desired. Pinia-converted stores are kept in /stores to differentiate them as more stores are moved over. Reset logic was also
reconfigured in store/index to account for the new stores not being in the old Vuex store tree.

Signed-off-by: Tracy Workman tworkman08@gmail.com


Open with GitKraken

To support potential future work to move to Vue 3,
Vuex needs to be replaced with Pinia. I started with
a couple of easy stores (analysis and wait). Pinia
convention recommends a flatter file structure than
was needed with Vuex. However, I still kept the stores
separated by folder to facilitate breaking them out
into actions and getters if desired. Pinia-converted
stores are kept in /stores to differentiate them as
more stores are moved over. Reset logic was also
reconfigured in store/index to account for the new
stores not being in the old Vuex store tree.

Signed-off-by: Tracy Workman <tworkman08@gmail.com>

@pedrolamas pedrolamas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed base cdefdcbd → head ac1f4f80. All 13 wait/* and analysis/* call sites are converted — no dangling $typedDispatch('wait/...'), $typedGetters['wait/...'] or dispatch: strings remain, and no spec touches these stores. Pinia activation ordering is correct: Vue.use(PiniaVuePlugin) runs at import of src/stores/pinia.ts, setActivePinia fires in the root beforeCreate during new Vue({ pinia }), and every useXStore() call site is lazy and post-mount (appInit() runs after $mount). vue-demi is already in pnpm-workspace.yaml allowBuilds, so its postinstall switch runs. No crash-level bugs found.

Assessment: performance and maintainability going forward

Performance — essentially neutral, slightly negative on bundle. Pinia 2 on Vue 2.7 sits on the same reactivity core (vue-demi → Vue 2.7's reactive/computed), so waits tracking behaves identically to the Vuex getters it replaces — I traced the hasWait method-style getter and confirmed the render watcher still collects state.waits at call time, so there's no reactivity regression. Costs added: ~5 kB gzip for pinia plus a duplicated vue-demi (see inline comment on the lockfile), Vuex stays resident for the whole migration so both systems ship simultaneously, and useStore() resolution moves onto the socket hot path and widget render paths. @vue/devtools-api should tree-shake in prod via Vite's process.env.NODE_ENV replacement, but worth confirming in a bundle report. None of this is material at Fluidd's scale; the honest summary is "no perf win, small perf tax, paid for architectural reasons".

Maintainability — right direction, but the seam needs hardening before it scales. Two stores out of 28 is a good, low-risk pilot and the conversions are faithful. The concern is the cost of the in-between state, which will last many PRs:

  1. The hand-maintained reset registry fails silently (see src/store/index.ts) — this is the one thing I'd fix before merging, since it's the mechanism that will actually break as migration proceeds.
  2. Two side-effect dispatch mechanisms now coexist in socketActions.ts, which will multiply once printer/files move.
  3. Migrated state loses Vuex's dev-only strict mutation guard, with no Pinia equivalent.
  4. src/store vs src/stores is a standing typo hazard (one character apart, both resolve).

Recommend landing this with an explicit migration order, a typed reset registry, and a decision on whether NotifyOptions.dispatch/commit are being retired — otherwise the half-migrated state becomes the steady state.

Comment thread src/store/index.ts Outdated
Comment thread src/api/socketActions.ts Outdated
Comment thread src/api/socketActions.ts Outdated
Comment thread src/plugins/socketClient.ts
Comment thread src/mixins/state.ts
Comment thread src/stores/analysis.ts Outdated
state: (): AnalysisState => ({
status: null,
}),
getters: {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

low. Empty getters: { } block is noise.

Also worth noting status is write-only: nothing in src/ reads useAnalysisStore().status, and serverAnalysisStatus (its only writer) has zero callers. Pre-existing dead state, but the migration is a good moment to delete it rather than port it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed getters, status, serverAnalysisStatus as suggested. the only outstanding action on useAnalysisStore() is now onAnalysisProcess - im wondering if it should be left as-is or folded into files, which is the only consumer, and dropping the analysis store entirely.

Comment thread src/stores/analysis.ts Outdated
import { TinyColor } from '@ctrl/tinycolor'
import dbKey from '@/util/db-key'
import { useWaitStore } from '../../stores/wait'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

low. Relative '../../stores/wait' here, but @/stores/analysis in src/store/index.ts (and '../stores/wait' on the line above it) — three styles for the same target.

Given src/stores/ sits one character away from the existing src/store/, mixed relative paths between the two trees are a real typo hazard: ../stores/… mistyped as ../store/… resolves to a different, existing directory and silently imports the wrong thing. Suggest standardising on @/stores/… everywhere, and possibly a less collision-prone directory name (src/pinia/).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yup, that's my error, ill standardize to '@/'

I see valid concern here about store/stores. I took the naming from Pinia's migration guide regarding stores. End state @/stores makes a more descriptive folder structure for maintainability long term and fits the style of the rest of the code base where folders are generally descriptive by function rather than library. How would you like to proceed?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you, please continue with @/stores plan, and keep an eye to make sure there are no leftovers on @/store after migration.

Comment thread src/stores/wait.ts
Comment thread pnpm-lock.yaml
@pedrolamas

Copy link
Copy Markdown
Member

Hi @tworkman08, thank your for this Pull Request.

This looks quite promising and I like it as a first approach to Pinia!

I've pointed my Claude to this and it has posted a few comments from a first review that will need to be resolved.

I would also prefer to avoid .then() when possible and instead use modern async... await where possible, so that might be a good target of refactoring too!

@tworkman08

Copy link
Copy Markdown
Contributor Author

Hi @tworkman08, thank your for this Pull Request.

This looks quite promising and I like it as a first approach to Pinia!

I've pointed my Claude to this and it has posted a few comments from a first review that will need to be resolved.

I would also prefer to avoid .then() when possible and instead use modern async... await where possible, so that might be a good target of refactoring too!

Thanks for the detailed reply @pedrolamas, I definitely wanted to start slow and easy. especially since I made assumptions during the initial pr write and wanted to get feedback on the approach. I'll take a look at the reviews above and make the necessary updates shortly.

Addresses feedback from review on moved stores wait
and analysis to pinia pr.

File structure and naming now follows the reccomended
structure from Pinia, rather than the previous vuex model:
https://pinia.vuejs.org/cookbook/migration-vuex.html#Restructuring-Modules-to-Stores

Standardized paths to @/ rather than relative paths.

Signed-off-by: Tracy Workman <tworkman08@gmail.com>
Adressing review feedback:
- Created plugin to register pinia stores on creation +
  added import.meta.glob to eagerly instansiates Stores.
  creating a single source of truth without having to manually
  add stores to a list.
- updated reset function to resetPiniaStores() iterating
  the actual plugin registry above, so a store can't be migrated
  to pinia and silently skipped on reset
- dded check in reset when a reset key matches
  neither a Vuex nor a pinia store.
- Replace serverAnalysisProcess's unconditional .then()
  with a declarative pinia: option on NotifyOptions,
  mirroring dispatch:/commit: and staying overridable by callers
  this is expandable to typedDispatch/commit as well
- Lazily cache useWaitStore() in WebSocketClient and StateMixin
  instead of resolving it on every socket message/render
- Dropped dead code in stores/analysis.ts: onAnalysisStatus, it's
  corresponding caller in SocketActions.ts, and Analysis Status
  which was set, but never read.

  Signed-off-by: Tracy Workman <tworkman08@gmail.com>
pinned ^0.14.10 which is compatible with both
echarts and pinia.

signed-off-by: Tracy Workman <tworkman08@gmail.com>
To support potential future work to move to Vue 3,
Vuex needs to be replaced with Pinia. I started with
a couple of easy stores (analysis and wait). Pinia
convention recommends a flatter file structure than
was needed with Vuex. However, I still kept the stores
separated by folder to facilitate breaking them out
into actions and getters if desired. Pinia-converted
stores are kept in /stores to differentiate them as
more stores are moved over. Reset logic was also
reconfigured in store/index to account for the new
stores not being in the old Vuex store tree.

Signed-off-by: Tracy Workman <tworkman08@gmail.com>
Addresses feedback from review on moved stores wait
and analysis to pinia pr.

File structure and naming now follows the reccomended
structure from Pinia, rather than the previous vuex model:
https://pinia.vuejs.org/cookbook/migration-vuex.html#Restructuring-Modules-to-Stores

Standardized paths to @/ rather than relative paths.

Signed-off-by: Tracy Workman <tworkman08@gmail.com>
Adressing review feedback:
- Created plugin to register pinia stores on creation +
  added import.meta.glob to eagerly instansiates Stores.
  creating a single source of truth without having to manually
  add stores to a list.
- updated reset function to resetPiniaStores() iterating
  the actual plugin registry above, so a store can't be migrated
  to pinia and silently skipped on reset
- dded check in reset when a reset key matches
  neither a Vuex nor a pinia store.
- Replace serverAnalysisProcess's unconditional .then()
  with a declarative pinia: option on NotifyOptions,
  mirroring dispatch:/commit: and staying overridable by callers
  this is expandable to typedDispatch/commit as well
- Lazily cache useWaitStore() in WebSocketClient and StateMixin
  instead of resolving it on every socket message/render
- Dropped dead code in stores/analysis.ts: onAnalysisStatus, it's
  corresponding caller in SocketActions.ts, and Analysis Status
  which was set, but never read.

  Signed-off-by: Tracy Workman <tworkman08@gmail.com>
pinned ^0.14.10 which is compatible with both
echarts and pinia.

signed-off-by: Tracy Workman <tworkman08@gmail.com>
https://github.com/tworkman08/fluidd into pinia-refactor

signed-off-by: Tracy Workman <tworkman08@gmail.com>
signed-off-by: Tracy Workman <tworkman08@gmail.com>
Remove outdated and redundant comments to enhance code understanding.
Update the `usePiniaStore` helper's comment to accurately describe its behavior
with unmatched Vuex namespace/actions. Apply minor formatting adjustments for
improved readability.

Signed-off-by: Tracy Workman <tworkman08@gmail.com>
@tworkman08

Copy link
Copy Markdown
Contributor Author

@pedrolamas - I believe I've addressed the concerns you've presented so far. I would love if you'd take a look at the updated branch and let me know if you have further feedback.

Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>

# Conflicts:
#	pnpm-lock.yaml
#	src/plugins/socketClient.ts
#	src/store/config/actions.ts
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Bundle size report (gzip)

Chunk Base Head Δ
assets/wait-*.js 🆕 6.8 kB +6.8 kB
assets/lib-*.js 🆕 134 B +134 B
assets/index-*.js 161.0 kB 160.9 kB -118 B
assets/FileSystem-*.js 31.2 kB 31.3 kB +97 B
assets/vue-vendor-*.js 47.3 kB 47.3 kB +33 B
sw.js 11.2 kB 11.2 kB +29 B
assets/socketActions-*.js 39.4 kB 39.4 kB -24 B
assets/WebrtcMediamtxCamera-*.js 1.8 kB 1.8 kB -8 B
assets/Console-*.js 370 B 363 B -7 B
assets/Dashboard-*.js 63.2 kB 63.2 kB +7 B
assets/WebrtcGo2RtcCamera-*.js 1.5 kB 1.5 kB -7 B
assets/vuetify-*.js 130.9 kB 130.9 kB +6 B
assets/AppColorPicker-*.js 13.0 kB 13.0 kB -5 B
assets/AppTextField-*.js 902 B 897 B -5 B
assets/NotFound-*.js 439 B 434 B -5 B
assets/_plugin-vue2_normalizer-*.js 2.2 kB 2.2 kB -4 B
assets/dynamicImports-*.js 1.1 kB 1.1 kB +4 B
assets/AppSettingsNav-*.js 816 B 813 B -3 B
assets/JobQueueCard-*.js 4.5 kB 4.5 kB -3 B
assets/vue-echarts-chunk-*.js 315.2 kB 315.2 kB +3 B
assets/AfcPrintStartDialogTool-*.js 2.4 kB 2.4 kB +2 B
assets/AppInlineChart-*.js 643 B 641 B -2 B
assets/AppNamedSlider-*.js 1.7 kB 1.7 kB -2 B
assets/BeaconCard-*.js 4.3 kB 4.3 kB -2 B
assets/ConsoleCard-*.js 2.0 kB 2.0 kB -2 B
assets/DeviceCamera-*.js 1.0 kB 1.0 kB -2 B
assets/Diagnostics-*.js 16.0 kB 16.0 kB -2 B
assets/file-data-transfer-*.js 408 B 410 B +2 B
assets/GcodePreview-*.js 369 B 367 B -2 B
assets/History-*.js 3.8 kB 3.8 kB -2 B
assets/Icons-*.js 528 B 530 B +2 B
assets/Timelapse-*.js 1.9 kB 1.9 kB +2 B
assets/AppBtnCollapseGroup-*.js 640 B 639 B -1 B
assets/AppChart-*.js 754 B 753 B -1 B
assets/Configure-*.js 775 B 776 B +1 B
assets/DiskUsageCard-*.js 3.2 kB 3.2 kB +1 B
assets/FileSystem-*.css 724 B 725 B +1 B
assets/FullscreenCamera-*.js 425 B 424 B -1 B
assets/GcodePreviewCard-*.js 13.2 kB 13.2 kB -1 B
assets/HlsstreamCamera-*.js 172.8 kB 172.8 kB -1 B
assets/IframeCamera-*.js 553 B 552 B -1 B
assets/JobHistoryItemStatus-*.js 1.2 kB 1.2 kB -1 B
assets/Jobs-*.js 499 B 500 B +1 B
assets/MjpegstreamerAdaptiveCamera-*.js 1.0 kB 1.0 kB +1 B
assets/System-*.js 2.3 kB 2.3 kB -1 B
assets/TimelapseRenderSettingsDialog-*.js 1.8 kB 1.8 kB +1 B
assets/Tune-*.js 3.4 kB 3.4 kB +1 B
assets/Watch-*.js 328 B 327 B -1 B
assets/WebrtcCamerastreamerCamera-*.js 1.5 kB 1.5 kB -1 B
Total 2.9 MB 2.9 MB +7.0 kB

121 chunks compared, 49 changed. Sizes are gzip, matching what nginx serves.

pedrolamas and others added 3 commits August 31, 2026 20:05
Invert ownership of the analysis socket call: the store awaits
SocketActions itself instead of the API layer naming a handler by
string. This drops NotifyOptions.pinia, the store registry lookup and
its helpers, and keeps socketActions.ts free of store imports (a typed
callback there would have been circular).

Reset is now typed too: the root reset payload is (keyof RootState)[],
so a stale module name is a compile error rather than a silent skip.
Pinia stores are reset in full when no payload is given, and explicitly
by their owner otherwise, which removes the need to eagerly instantiate
every store at startup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YXJV5dJMQHee58crfm46M
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
Time analysis is fired per file without awaiting, so a Moonraker error
or a mid-flight socket drop surfaced as one unhandled rejection per
file. Log through consola instead; the user-facing toast already comes
from the global socket error handler.

Also adds specs for resetPiniaStores, covering both an options store
and a setup store providing its own $reset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018n862D2eUnE6eV22wAydSS
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
The memoised waitStore getters in StateMixin and WebSocketClient cached
what is only a map lookup, duplicating the same pattern across two files
with two different typings. Call useWaitStore() directly instead, and
bind a local only where one function uses a store more than once.

Also drops Moonraker.Analysis.StatusResponse, orphaned when
serverAnalysisStatus was removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018n862D2eUnE6eV22wAydSS
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants