Skip to content

Vue 3 Migration#139

Draft
MarkH817 wants to merge 121 commits intoeternagame:devfrom
MarkH817:feature/vue-3-migration
Draft

Vue 3 Migration#139
MarkH817 wants to merge 121 commits intoeternagame:devfrom
MarkH817:feature/vue-3-migration

Conversation

@MarkH817
Copy link
Copy Markdown

@MarkH817 MarkH817 commented Mar 10, 2026

Summary

  • Architecture rework
    • Migrate to Vue 3
    • Replace Vue class components to Composition API with <script setup> syntax
    • Keeping as much chat logic in Pinia stores or utility functions as possible
    • Replace Vuex with Pinia
      • Breakdown chat.vuex.ts logic into separate stores (and preventing circular dependencies)
      • irc.store.ts manages client connection and exposes client to other stores only after the client has been registered
      • channel.store.ts manages channel list and chat history
      • chat.store.ts handles chat inputs and calling slash-commands
    • Swap to bootstrap-vue-next for Vue 3 compatibility
    • Add @vueuse/core to handle browser/DOM API interactions
    • Move slash-command logic to a registry pattern (some commands still are in development)
    • Replace deprecated / abandoned libraries
    • Update Browsers List target to defaults
  • Tool changes
    • Upgrade to ESLint 9
    • Add Oxlint, and Prettier
    • Add Vite
    • Add Vitest for component and utility unit test capability
    • Add lint-staged / husky pre-commit hook
  • Vite related plugins/presets
    • vite-plugin-node-polyfills to allow irc-framework usage
    • postcss-preset-env to convert modern CSS syntax for targeted browsers
    • @vitejs/pluin-legacy to add JS polyfills to the build as needed
  • Update irc-framework type definitions for more events/features used
    • Adding tags parameter since Ergo supports message-tags
      • Timestamp
      • Message ID
      • Label
      • Batch
      • Client tags
    • Using reconnection logic already provided by the library
    • Adding other events and updating as needed

Additional Notes

  • Other than ./src/app/types/modules/irc-framework, files in ./src/app are not in use in the Vue 3 build
    • Will eventually remove this directory once the remaining features are migrated in later PRs

- Gracefully handle cases when chat user is anonymous or fetch has failed
- Update `getUserInfo()` and `getPuzzleInfo()` to use `fetch()`
- Add default avatar image
- Add native import subpath to `#store` to future-proof path aliases
- Reference exported chat/settings store proxy in Composition API components
- Add ts-expect-error messages around type conflicts in Vue 2 for SFC Composition API
- Replace id attributes with class style identifier
- Will need to refactor to decouple it from App.vue
- Swap type from enum to string union
- Fix interactions with this component to use the correct type
- Swapped invalid uses of <li> with <div>
- Added note to remove `vxm.chat.tabbing` since `:focus-visible` is available in CSS for this scenario
- Adjusted NotificationSection component to check if a channel reference is defined before interacting
- Replace use of id/<li> with class/<div> respectively
- Previously was forcing at least 1 keyword to exist
- Removes $refs and $parent interactions between color picker and sliders
- Convert to Composition API
- Reference new Settings store
- Update/retrieve markdown button setting with `useLocalStorage()`
- Remove Vue libraries not available in Vue 2
- Remove irrelevant test files
- Add Bootstrap Vue Next
- Remove Babel configuration
- Add new entry App.vue
- Updated to ESLint 9
- Move lint-staged from package.json to file
- Update irc-framework websocket type definition
- Store static/constant values under `src/constants` directory
- Add `src/models` directory and use `interface`/`type` instead of `class`/`enum`
- Move more settings previously from Chat Vuex store to settings or other stores
- Created irc store to solely hold irc client, login actions, and connection status
- Add `src/utils` for holding logic independent from Vue rendering
- Restore `stream-browserify` aliasing needed for `irc-framework`
- Add `vite-plugin-node-polyfills` for `irc-framework` since it uses Node-only modules
  - Was previously handled automatically by `webpack`
- Updated env variable names
- Switch User model nicks from `string[]` to `Set<string>`
- Share connection status and reconnection info from IRC client
MarkH817 added 20 commits April 3, 2026 20:48
- If this settings is enabled, client will set self as AWAY after 1 minute of inactivity
- Ignore idle check if user manually sets self as AWAY
- Add RegEx and Link attribute plugins for markdown-it
- Ensure blockquotes and highlighted text escape unsafe HTML
- Add `data-link-type` to indicate internal/externally handled links
- Removed cursive and serif formatting since it keeps formatting timestamps unexpectedly
- Changed user/channel mentions into buttons for a11y
- Prepare listener for puzzle link detection
- Attach tooltip to recently focused/hovered puzzle link
- Cache puzzle data in store
- Prevent duplicate fetches if puzzle is loading or cached
- Add confirmation modal store
- Add `type-fest` for utility types
- Conditionally render mentioned channels as buttons
- Navigate to channel on click, if it is a joined channel
- Remove tagged player button interaction
- Prevents accidental click/tab with preview links or buttons
- Allows renderer to parse blockquotes, rather than use RegEx plugin
- Future-proofing whenever multi-line messages are implemented
- Displays available commands or command details
Copy link
Copy Markdown
Member

@luxaritas luxaritas left a comment

Choose a reason for hiding this comment

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

Lots of really great work here. Mostly just have relatively minor nitpicks/issues I caught, and some questions for clarification. (nit --> nitpick, quickfix --> proposal for a super minor change, fyi/thanks --> just a note)

Comment thread eslint.config.ts
Comment on lines +18 to +23
...pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
{
...pluginVitest.configs.recommended,
files: ['src/**/__tests__/*', 'src/**/*.{spec,test}.*'],
},
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.

Quickfix: Do you think it's viable to omit eslint entirely? According to oxc-project/oxc#479, oxc-project/oxc#2180, and oxc-project/oxc#11440 the only rules which aren't implemented are the few vue rules that rely on template parsing, which seem rather minimal.

Also thoughts about oxfmt instead of prettier? It should be dropin, is now out of alpha, and even though its beta it seems stable (with full prettier conformance).

:state="uidError.length === 0"
:invalid-feedback="uidError"
>
<BFormInput name="uid" type="text" inputmode="numeric" v-model.trim="form.uid" required />
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.

Thanks: TIL about inputmode!

Comment thread src/components/LoginForm.vue Outdated
});

function onSubmit() {
emit('login', toRaw(form));
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.

Nit: I don't have a strong opinion on this, but I'm wondering if it makes more sense to just interact with the store directly. Rationale:

  • Its more consistent with the fact that elsewhere for similar behavior we delegate to the store
  • The login form vs chat app feels like a "routing" type of behavior
  • It doesn't feel like this state or its lifetime is clearly encapsulated

But my first reaction was not being sure what the best practice would be! Interested to hear rationale in the other direction

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I agree with this take. This was just a port of the existing login form. I'll make this change. 👍

Comment thread src/components/ui/LoadingSpinner.vue Outdated
<template>
<img
alt="Loading"
src="https://s3.amazonaws.com/eterna/icon_img/loading.gif"
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.

Quickfix/Nit: This is admittedly kinda crusty. Minimally I think itd make sense to have all resources actually in this repo instead of static CDN links, but given we're using bootstrap-vue we could just use BSpinner

'minimization-triangle--closed': !open,
}"
type="button"
:style="{ backgroundImage: `url(${arrowImage})` }"
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.

FYI: You can use v-bind in the style block: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css

Comment thread src/components/sidebar/SidebarMenu.vue Outdated
<SidebarSectionButton
@click="activeTab = 'user'"
:active="activeTab === 'user'"
label="User"
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.

Suggested change
label="User"
label="Online Users"

<template v-if="isAttemptingConnection">
<BAlert class="m-0" v-if="irc.connectionStatus === 'connecting'" :model-value="true">
<img
src="https://s3.amazonaws.com/eterna/icon_img/loading.gif"
Copy link
Copy Markdown
Member

@luxaritas luxaritas Apr 21, 2026

Choose a reason for hiding this comment

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

Quickfix/Nit: We have a component for this, yea?

Comment thread src/components/layout/header/StarButton.vue
}

const [username, targetChannel, ...reasonParts] = args;
const channels = targetChannel === '*' ? stores.channel.channelNameList : [targetChannel];
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.

What about channels the moderator isn't currently in? This is another argument for whitelisting channels. (nb: this would be solved with something like a KLINE, but Ergo doesn't currently support that for mutes :( )

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I see this tying back to the earlier point of not allowing users to create their own channels arbitrarily. I can adjust the * input to point at refer to approved/whitelisted channels instead.

},
};

export const banmask: CommandHandler = {
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.

Proposal: Should there be a mutemask/unmutemask too?

@MarkH817 MarkH817 marked this pull request as draft April 27, 2026 19:09
- Only require #general chat in default list
- The #test channel will only be shown in development mode
- Removing both settings input and slash-command for creating arbitrary channels
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