diff --git a/.github/workflows/nicolium.yaml b/.github/workflows/nicolium.yaml
index 6cd8edf1c..ba1f3d7f4 100644
--- a/.github/workflows/nicolium.yaml
+++ b/.github/workflows/nicolium.yaml
@@ -3,6 +3,7 @@ name: Nicolium CI
on:
push:
branches: [ "develop", "semistable" ]
+ tags: [ "v*" ]
pull_request:
branches: [ "develop" ]
@@ -69,24 +70,35 @@ jobs:
working-directory: ./packages/nicolium/dist
run: zip -r ../nicolium.zip .
+ - name: Upload release zip artifact
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: actions/upload-artifact@v4
+ with:
+ name: nicolium-release-zip
+ path: packages/nicolium/nicolium.zip
+
- name: build
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
env:
NODE_ENV: production
working-directory: ./packages/nicolium
run: |
- BANNER_HTML="pl.mkljczk.pl runs Nicolium's \`develop\` branch, which can break sometimes. For a more stable experience, use web.nicolium.app." WITH_LANDING_PAGE=true pnpm build
+ BANNER_HTML="pl.mkljczk.pl runs Nicolium's \`develop\` branch, which can break sometimes. For a more stable experience, use web.nicolium.app." WITH_LANDING_PAGE=true ANALYZE=true pnpm build
cp dist/index.html dist/404.html
cp nicolium.zip dist/pl-fe.zip
- name: Build pl-api documentation
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
working-directory: ./packages/pl-api
run: npx typedoc
- name: Copy pl-api documentation
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
working-directory: ./packages/pl-api
run: cp docs ../nicolium/dist/pl-api-docs -r
- name: Create docs redirect
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
working-directory: .
run: |
mkdir -p ./packages/nicolium/dist/docs
@@ -96,11 +108,31 @@ jobs:
EOF
- name: Upload Github Pages artifact
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: actions/upload-pages-artifact@v3
with:
name: github-pages
path: packages/nicolium/dist
+ release:
+ needs: build
+ if: startsWith(github.ref, 'refs/tags/')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Download release zip artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: nicolium-release-zip
+ path: .
+
+ - name: Upload asset to GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ files: nicolium.zip
+ generate_release_notes: true
+
deploy:
needs: build
if: github.ref == 'refs/heads/develop'
diff --git a/docs/installing/mitra.md b/docs/installing/mitra.md
index d907aca01..9f3d8615c 100644
--- a/docs/installing/mitra.md
+++ b/docs/installing/mitra.md
@@ -9,7 +9,7 @@ order: 31
Installing Nicolium as a frontend for Mitra is no different from installing the default Mitra Web frontend. Just extract the Nicolium files into the directory specified in `config.yaml` under `web_client_dir`, by default `/usr/share/mitra/www`.
```bash
-curl -O https://pl.mkljczk.pl/pl-fe.zip
-unzip pl-fe.zip -d /usr/share/mitra/www
-rm pl-fe.zip
+curl -O https://web.nicolium.app/release.zip
+unzip release.zip -d /usr/share/mitra/www
+rm release.zip
```
\ No newline at end of file
diff --git a/docs/installing/pleroma-akkoma.md b/docs/installing/pleroma-akkoma.md
index ad9ab2858..b5c5d154b 100644
--- a/docs/installing/pleroma-akkoma.md
+++ b/docs/installing/pleroma-akkoma.md
@@ -11,34 +11,34 @@ order: 32
The most straightforward way to install Nicolium as a frontend for Pleroma or Akkoma is to simply download it and place its files in the `/instance/static` directory of your Pleroma/Akkoma installation (usually `/opt/pleroma/instance/static` or `/opt/akkoma/instance/static`, accordingly).
```bash
-curl -O https://pl.mkljczk.pl/pl-fe.zip
-unzip pl-fe.zip -d /opt/pleroma/instance/static/
-rm pl-fe.zip
+curl -O https://web.nicolium.app/release.zip
+unzip release.zip -d /opt/pleroma/instance/static/
+rm release.zip
```
## Installation via Pleroma/Akkoma frontend management
-It is also possible to use the Pleroma/Akkoma frontend management tool. You can find more information about it in the [Pleroma documentation](https://docs.pleroma.social/backend/administration/frontends-management/). You can use either the PleromaFE built-in admin dashboard or the older AdminFE to install Nicolium and set it as the server frontend. You don't have to provide any URL. It's right there in Pleroma/Akkoma.
+It is also possible to use the Pleroma/Akkoma frontend management tool. You can find more information about it in the [Pleroma documentation](https://docs.pleroma.social/backend/administration/frontends-management/). You can use either the PleromaFE built-in admin dashboard or the older AdminFE to install Nicolium and set it as the server frontend. You don't have to provide any URL. It's right there in Pleroma/Akkoma (under the name `pl-fe`).
To install it from CLI, use:
### OTP
```bash
-./bin/pleroma_ctl frontend install pl-fe --ref develop --build-url https://pl.mkljczk.pl/pl-fe.zip --build-dir .
+./bin/pleroma_ctl frontend install nicolium --ref release --build-url https://web.nicolium.app/release.zip --build-dir .
```
### From Source
```bash
-mix pleroma.frontend install pl-fe --ref develop --build-url https://pl.mkljczk.pl/pl-fe.zip --build-dir .
+mix pleroma.frontend install nicolium --ref release --build-url https://web.nicolium.app/release.zip --build-dir .
```
It is now possible to set Nicolium as the primary frontend in the configuration file or via AdminFE:
```elixir
config :pleroma, :frontends,
primary: %{
- "name" => "pl-fe",
- "ref" => "develop"
+ "name" => "nicolium", # Use `pl-fe` if you installed it via Pleroma/Akkoma dashboard
+ "ref" => "release"
},
...
```
diff --git a/docs/installing/standalone.md b/docs/installing/standalone.md
index fe569fc4d..52b881ef9 100644
--- a/docs/installing/standalone.md
+++ b/docs/installing/standalone.md
@@ -19,4 +19,4 @@ nicolium.example.com {
}
```
-This assumes you're serving Nicolium under the nicolium.example.com domain and the Nicolium files are located in `/var/www/nicolium`. You can download Nicolium from `https://pl.mkljczk.pl/pl-fe.zip` or [build it from source](../building/nicolium.md).
\ No newline at end of file
+This assumes you're serving Nicolium under the nicolium.example.com domain and the Nicolium files are located in `/var/www/nicolium`. You can download Nicolium from `https://web.nicolium.app/release.zip` or [build it from source](../building/nicolium.md).
\ No newline at end of file
diff --git a/package.json b/package.json
index 58f66bbf6..c1fd97b1b 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"@oxfmt/binding-linux-x64-gnu": "^0.36.0",
"@oxlint/binding-linux-x64-gnu": "^1.51.0",
"husky": "^9.0.0",
- "lint-staged": "^16.2.7"
+ "lint-staged": "^16.4.0"
},
"resolutions": {
"@types/react": "^19.2.14",
diff --git a/packages/nicolium/CHANGELOG.md b/packages/nicolium/CHANGELOG.md
index a000c5ed1..19699d1e8 100644
--- a/packages/nicolium/CHANGELOG.md
+++ b/packages/nicolium/CHANGELOG.md
@@ -6,7 +6,7 @@
### Major changes
-- Switched to a separate library [`pl-api`](https://codeberg.org/nicolium/nicolium/src/branch/develop/packages/pl-api) for Mastodon API integration. It is mostly written from scratch, inheriting minor code parts from Soapbox/Mastodon. This also comes with improved compatibility with various Mastodon API extensions and abstracts out the implementation details.
+- Switched to a separate library [`pl-api`](https://codeberg.org/nicolium/nicolium/src/packages/pl-api) for Mastodon API integration. It is mostly written from scratch, inheriting minor code parts from Soapbox/Mastodon. This also comes with improved compatibility with various Mastodon API extensions and abstracts out the implementation details.
- Migrated client data stores from Redux to Zustand and remote data stores to TanStack Query. Stores have been migrated away from `immutable`.
- Migrated router from React Router to TanStack Router.
- Styles are being migrated from TailwindCSS to SCSS.
diff --git a/packages/nicolium/index.html b/packages/nicolium/index.html
index a2ddb8c49..04fd3aa79 100644
--- a/packages/nicolium/index.html
+++ b/packages/nicolium/index.html
@@ -4,7 +4,7 @@
diff --git a/packages/nicolium/package.json b/packages/nicolium/package.json
index cb509a6ab..2e57711fc 100644
--- a/packages/nicolium/package.json
+++ b/packages/nicolium/package.json
@@ -41,31 +41,30 @@
"@fontsource/noto-sans-javanese": "^5.2.8",
"@fontsource/roboto-mono": "^5.2.8",
"@fontsource/tajawal": "^5.2.7",
- "@lexical/code": "^0.41.0",
- "@lexical/hashtag": "^0.41.0",
- "@lexical/link": "^0.41.0",
- "@lexical/list": "^0.41.0",
- "@lexical/markdown": "^0.41.0",
- "@lexical/react": "^0.41.0",
- "@lexical/rich-text": "^0.41.0",
- "@lexical/selection": "^0.41.0",
- "@lexical/utils": "^0.41.0",
+ "@lexical/code": "^0.42.0",
+ "@lexical/hashtag": "^0.42.0",
+ "@lexical/link": "^0.42.0",
+ "@lexical/list": "^0.42.0",
+ "@lexical/markdown": "^0.42.0",
+ "@lexical/react": "^0.42.0",
+ "@lexical/rich-text": "^0.42.0",
+ "@lexical/selection": "^0.42.0",
+ "@lexical/utils": "^0.42.0",
"@mkljczk/url-purify": "^0.0.5",
"@phosphor-icons/core": "^2.1.1",
"@reach/combobox": "^0.18.0",
"@reach/rect": "^0.18.0",
"@reach/tabs": "^0.18.0",
"@react-spring/web": "^10.0.3",
- "@reduxjs/toolkit": "^2.11.2",
- "@sentry/browser": "^10.43.0",
- "@sentry/core": "^10.43.0",
- "@sentry/react": "^10.43.0",
+ "@sentry/browser": "^10.45.0",
+ "@sentry/core": "^10.45.0",
+ "@sentry/react": "^10.45.0",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19",
- "@tanstack/react-pacer": "^0.20.0",
- "@tanstack/react-query": "^5.90.21",
- "@tanstack/react-router": "^1.167.4",
+ "@tanstack/react-pacer": "^0.21.0",
+ "@tanstack/react-query": "^5.94.5",
+ "@tanstack/react-router": "^1.168.2",
"@transfem-org/sfm-js": "^0.26.1",
"@twemoji/svg": "^15.0.0",
"@uidotdev/usehooks": "^2.4.1",
@@ -91,7 +90,7 @@
"intl-messageformat": "^11.1.2",
"intl-pluralrules": "^2.0.1",
"leaflet": "^1.9.4",
- "lexical": "^0.41.0",
+ "lexical": "^0.42.0",
"line-awesome": "^1.3.0",
"localforage": "^1.10.0",
"lodash-es": "^4.17.23",
@@ -122,7 +121,7 @@
"stringz": "^2.1.0",
"tabbable": "^6.4.0",
"use-mutative": "^1.3.1",
- "valibot": "^1.2.0",
+ "valibot": "^1.3.1",
"zustand": "^5.0.12",
"zustand-mutative": "^1.3.1"
},
@@ -142,30 +141,30 @@
"@types/react-dom": "^19.2.3",
"@types/react-sparklines": "^1.7.5",
"@types/react-swipeable-views": "^0.13.6",
- "@typescript/native-preview": "7.0.0-dev.20260317.1",
+ "@typescript/native-preview": "7.0.0-dev.20260322.1",
"@vitejs/plugin-react": "^6.0.0",
"@vitest/coverage-v8": "4.1.0",
"eslint-plugin-formatjs": "^6.3.0",
"globals": "^17.4.0",
- "jsdom": "^29.0.0",
+ "jsdom": "^29.0.1",
"oxfmt": "^0.41.0",
"oxlint": "^1.56.0",
- "oxlint-tsgolint": "^0.17.0",
+ "oxlint-tsgolint": "^0.17.1",
"rollup-plugin-bundle-stats": "^4.22.0",
- "stylelint": "^17.4.0",
+ "stylelint": "^17.5.0",
"stylelint-config-clean-order": "^8.0.1",
"stylelint-config-standard-scss": "^17.0.0",
"stylelint-order": "^8.0.0",
"tailwindcss": "^3.4.19",
"tslib": "^2.8.1",
- "type-fest": "^5.4.4",
+ "type-fest": "^5.5.0",
"typescript": "5.9.3",
- "vite": "^8.0.0",
+ "vite": "^8.0.1",
"vite-plugin-compile-time": "^0.4.6",
"vite-plugin-html": "^3.2.2",
"vite-plugin-pwa": "^1.2.0",
"vite-plugin-require": "^1.2.14",
- "vite-plugin-static-copy": "^3.3.0",
+ "vite-plugin-static-copy": "^3.4.0",
"vitest": "^4.1.0"
},
"lint-staged": {
diff --git a/packages/nicolium/src/columns/notifications.tsx b/packages/nicolium/src/columns/notifications.tsx
index 42a2cfa40..3cddd6020 100644
--- a/packages/nicolium/src/columns/notifications.tsx
+++ b/packages/nicolium/src/columns/notifications.tsx
@@ -329,7 +329,7 @@ const NotificationsColumn: React.FC = ({ multiColumn, comp
placeholderCount={20}
onLoadMore={handleLoadOlder}
onScrollToTop={handleScrollToTop}
- listClassName={clsx('⁂-status-list', { 'animate-pulse': isLoading })}
+ listClassName={clsx('⁂-status-list', { 'no-reduce-motion:animate-pulse': isLoading })}
useWindowScroll={!multiColumn}
>
{scrollableContent!}
diff --git a/packages/nicolium/src/components/accounts/account-hover-card.tsx b/packages/nicolium/src/components/accounts/account-hover-card.tsx
index a1838ebb3..e63049958 100644
--- a/packages/nicolium/src/components/accounts/account-hover-card.tsx
+++ b/packages/nicolium/src/components/accounts/account-hover-card.tsx
@@ -1,4 +1,4 @@
-import { autoUpdate, flip, shift, useFloating, useTransitionStyles } from '@floating-ui/react';
+import { autoUpdate, flip, shift, useFloating } from '@floating-ui/react';
import iconCalendarDots from '@phosphor-icons/core/regular/calendar-dots.svg';
import iconTag from '@phosphor-icons/core/regular/tag.svg';
import { useRouter } from '@tanstack/react-router';
@@ -15,6 +15,7 @@ import Text from '@/components/ui/text';
import { useCurrentAccount } from '@/contexts/current-account-context';
import { UserPanel } from '@/features/ui/util/async-components';
import { useFrontendConfig } from '@/hooks/use-frontend-config';
+import { useTransitionStyles } from '@/hooks/use-transition-styles';
import { useAccountScrobbleQuery } from '@/queries/accounts/account-scrobble';
import { useAccount } from '@/queries/accounts/use-account';
import { usePatronUser } from '@/queries/patron/use-patron-user';
diff --git a/packages/nicolium/src/components/accounts/account.tsx b/packages/nicolium/src/components/accounts/account.tsx
index 5f2bd3cd7..0a7cd3cd7 100644
--- a/packages/nicolium/src/components/accounts/account.tsx
+++ b/packages/nicolium/src/components/accounts/account.tsx
@@ -216,7 +216,6 @@ const Account = ({
useLayoutEffect(() => {
const onResize = () => {
- console.log('resizing');
const style: React.CSSProperties = {};
const actionWidth = actionRef.current?.clientWidth || 0;
diff --git a/packages/nicolium/src/components/accounts/scrobble.tsx b/packages/nicolium/src/components/accounts/scrobble.tsx
index b5dcd3cb5..b1dc557c0 100644
--- a/packages/nicolium/src/components/accounts/scrobble.tsx
+++ b/packages/nicolium/src/components/accounts/scrobble.tsx
@@ -48,7 +48,7 @@ const Scrobble: React.FC = ({ scrobble }) => {
diff --git a/packages/nicolium/src/components/navigation/compose-button.tsx b/packages/nicolium/src/components/navigation/compose-button.tsx
index b2613a6f0..206db2ba5 100644
--- a/packages/nicolium/src/components/navigation/compose-button.tsx
+++ b/packages/nicolium/src/components/navigation/compose-button.tsx
@@ -31,11 +31,15 @@ const ComposeButton: React.FC = ({ shrink }) => {
const HomeComposeButton: React.FC = ({ shrink }) => {
const { openModal } = useModalsActions();
const onOpenCompose = () => {
- openModal('COMPOSE');
+ openModal('COMPOSE', undefined, document.getElementById('sidebar-compose') || undefined);
};
return (
-