Compare commits

...
63 Commits
Author SHA1 Message Date
Releases Homarr 60323b2651 chore(release): 1.5.0 [skip ci] 2025-02-07 19:13:19 +00:00
homarr-releases[bot] 93235cab6f chore(release): automatic release v1.5.0 2025-02-07 19:12:36 +00:00
homarr-renovate[bot] c849109df5 fix(deps): update dependency @tabler/icons-react to ^3.30.0 (#2266)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-07 14:46:12 +00:00
homarr-renovate[bot] 0da5a1b0fb fix(deps): update dependency ioredis to v5.5.0 (#2264)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-07 10:54:55 +00:00
homarr-renovate[bot] 7a8e671205 fix(deps): update testcontainers-node monorepo to ^10.18.0 (#2259)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-07 00:56:39 +00:00
homarr-renovate[bot] 3cbb656ee9 fix(deps): update dependency octokit to ^4.1.1 (#2262)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-07 00:56:14 +00:00
homarr-crowdin[bot] 55a493c475 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-02-07 00:44:51 +00:00
homarr-renovate[bot] 7bbbaf343b fix(deps): update dependency sass to ^1.84.0 (#2257)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-06 00:56:25 +00:00
homarr-crowdin[bot] 09b6323246 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-02-06 00:45:07 +00:00
homarr-renovate[bot] 3d0b319a88 fix(deps): update dependency drizzle-orm to ^0.39.2 (#2255)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-05 21:53:21 +00:00
ManuelandMeier Lukas 26468a4137 fix: foreign key reference default search engine (#2211)
* fix: foreign key reference default search engine

* fix: add sqlite migration to fix on delete action

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-02-05 11:36:42 +01:00
homarr-crowdin[bot] 62c83b79b4 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-02-05 00:44:34 +00:00
Manuel da09826393 fix: make system health temp optional (#2247) 2025-02-04 19:10:12 +01:00
ManuelandMeier Lukas 8058b6207a feat: add api key delete (#2210)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-02-04 17:14:15 +01:00
homarr-renovate[bot] c623a420ca fix(deps): update dependency trpc-to-openapi to ^2.1.3 (#2244)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-04 06:55:03 +00:00
homarr-renovate[bot] d5e36835f6 chore(deps): update dependency @types/node to ^22.13.1 (#2243)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-04 00:54:41 +00:00
homarr-crowdin[bot] 1f12a893ff chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-02-04 00:44:13 +00:00
Manuel bb67bf5fa2 feat: open all apps in category (#2212) 2025-02-03 21:28:01 +01:00
Jack WellerandMeier Lukas be603badfe feat: custom time/date format in clock widget (#2180)
* feat: custom time/date format in clock widget

* feat: add customFormat variables for old-imports

* fix: added missing comma for old-import

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-02-03 19:33:29 +01:00
homarr-crowdin[bot]andMeier Lukas 0d97cc50c2 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-02-03 18:24:58 +00:00
homarr-renovate[bot] 14a4b94911 chore(deps): update vitest monorepo to ^3.0.5 (#2227)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-03 18:23:27 +00:00
homarr-renovate[bot] 83044d23dd fix(deps): update dependency typescript-eslint to ^8.23.0 (#2232)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-03 18:22:17 +00:00
homarr-renovate[bot] 12456af101 chore(deps): update pnpm to v9.15.5 (#2224)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-03 18:19:36 +00:00
Meier Lukas 8c656febeb ci: fix corepack issue (#2237) 2025-02-03 19:10:49 +01:00
homarr-crowdin[bot] 2598ade925 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-02-02 00:46:10 +00:00
homarr-renovate[bot] 46cfb2383b fix(deps): update dependency semver-parser to ^4.1.8 (#2216)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-01 08:35:16 +00:00
homarr-renovate[bot] a385337268 fix(deps): update dependency jotai to ^2.11.3 (#2215)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-02-01 03:00:53 +00:00
homarr-crowdin[bot] bc79c6abab chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-02-01 00:47:18 +00:00
ManuelandMeier Lukas 5f36d8b125 feat: improve consistency and design (#1867)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-01-31 23:32:08 +01:00
homarr-renovate[bot] 6a9b65f219 chore(deps): update dependency @types/node to ^22.13.0 (#2208)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-31 20:18:06 +00:00
homarr-renovate[bot] 6ce7bc0ee2 fix(deps): update turbo monorepo to ^2.4.0 (#2207)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-31 19:40:11 +00:00
Meier Lukas 36c246cca8 chore(issue-template): add missing homarr version 1.4.0 2025-01-31 20:38:03 +01:00
Releases Homarr 3506f875e3 chore(release): 1.4.0 [skip ci] 2025-01-31 19:13:04 +00:00
homarr-releases[bot] e30e4b8cb7 chore(release): automatic release v1.4.0 2025-01-31 19:12:20 +00:00
homarr-renovate[bot] 9662e1abba fix(deps): update tiptap monorepo to v2.11.5 (#2203)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-31 09:33:25 +00:00
homarr-renovate[bot] e44c3a7bc8 fix(deps): update tanstack-query monorepo to ^5.66.0 (#2202)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-31 09:16:43 +00:00
homarr-renovate[bot] c287e9965f fix(deps): update dependency jotai to ^2.11.2 (#2201)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-31 02:14:23 +00:00
homarr-crowdin[bot] bea0124e3f chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-01-31 00:44:11 +00:00
Manuel d4154d259f fix: respect certificates for app ping (#2198) 2025-01-30 23:12:45 +01:00
Meier Lukas 77746278a3 fix(docker): missing production env variable (#2188) 2025-01-30 22:14:52 +01:00
Meier Lukas c75d4f0f58 fix: negative curve radius system health (#2197) 2025-01-30 22:14:26 +01:00
Meier Lukas 96fd5cc35e fix: use hostname for suspense query url (#2187) 2025-01-30 22:14:14 +01:00
Meier Lukas bd0b7c9518 fix: board crashes with empty string favicon (#2192) 2025-01-30 20:15:05 +01:00
homarr-renovate[bot] c05c51856b fix(deps): update tiptap monorepo to v2.11.4 (#2195)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-30 16:18:52 +00:00
homarr-crowdin[bot] dac1646ecc chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-01-30 00:43:11 +00:00
Yossi Hillali c19a4009f3 feat: weather widget - add wind speed, option to disable decimals for temperature & redesign dropdown forecast (#2099) 2025-01-29 23:02:13 +02:00
homarr-renovate[bot] 5657384d2a chore(deps): update dependency @types/swagger-ui-react to v5 (#2181)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-29 21:30:45 +01:00
homarr-renovate[bot] 4c72a34daf fix(deps): update dependency drizzle-orm to ^0.39.1 (#2186)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-29 16:39:45 +00:00
homarr-renovate[bot] de48ef21e9 chore(deps): update dependency @types/chroma-js to v3.1.1 (#2185)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-29 16:18:59 +00:00
homarr-renovate[bot] 37ccfab89a fix(deps): update dependency drizzle-kit to ^0.30.4 (#2183)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-29 15:43:18 +00:00
homarr-crowdin[bot] 07d60fe13e chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-01-29 00:44:09 +00:00
Meier Lukas bbd1b0c5d0 chore: remove node-options from example env 2025-01-28 22:26:44 +01:00
Meier Lukas c130e92661 chore(issue-template): add missing homarr version 1.3.1 2025-01-28 22:24:58 +01:00
Releases Homarr 0de853ff37 chore(release): 1.3.1 [skip ci] 2025-01-28 21:20:02 +00:00
homarr-releases[bot] a438a23f4e chore(release): automatic release v1.3.1 2025-01-28 21:19:19 +00:00
Manuel 8dd7fe297c fix: automatic search engine creation (#2172) 2025-01-28 22:18:31 +01:00
Manuel 780efab80c fix: app url line clamp (#2170) 2025-01-28 20:33:04 +01:00
homarr-renovate[bot] a4e9abcda1 fix(deps): update dependency swagger-ui-react to ^5.18.3 (#2164)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-28 13:00:40 +00:00
homarr-renovate[bot] 55b1afbb21 fix(deps): update tanstack-query monorepo to ^5.65.1 (#2161)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-28 09:17:32 +00:00
homarr-renovate[bot] b447fc9fe4 chore(deps): update dependency @types/node to ^22.12.0 (#2159)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-01-28 02:15:44 +00:00
homarr-crowdin[bot] 51ebaba399 chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2025-01-28 00:44:31 +00:00
homarr-renovate[bot]andMeier Lukas 9d4aea2c41 fix(deps): update dependency typescript-eslint to ^8.22.0 (#2149)
* fix(deps): update dependency typescript-eslint to ^8.22.0

* fix: lint issues

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-01-27 22:32:23 +01:00
Meier Lukas 4dcdac6621 chore(issue-template): add missing homarr version 1.3.0 2025-01-27 21:22:18 +01:00
106 changed files with 10662 additions and 2376 deletions

No files matched your search

+1 -4
View File
@@ -32,7 +32,4 @@ DB_URL='FULL_PATH_TO_YOUR_SQLITE_DB_FILE'
# If it is used, please use the full path to the directory where the certificates are stored. # If it is used, please use the full path to the directory where the certificates are stored.
# LOCAL_CERTIFICATE_PATH='FULL_PATH_TO_CERTIFICATES' # LOCAL_CERTIFICATE_PATH='FULL_PATH_TO_CERTIFICATES'
TURBO_TELEMETRY_DISABLED=1 TURBO_TELEMETRY_DISABLED=1
# Configure logging to use winston logger
NODE_OPTIONS='-r @homarr/log/override'
+3
View File
@@ -31,6 +31,9 @@ body:
label: Version label: Version
description: What version of Homarr are you running? description: What version of Homarr are you running?
options: options:
- 1.4.0
- 1.3.1
- 1.3.0
- 1.2.0 - 1.2.0
- 1.1.0 - 1.1.0
- 1.0.1 - 1.0.1
+55
View File
@@ -1,3 +1,58 @@
## [1.5.0](https://github.com/homarr-labs/homarr/compare/v1.4.0...v1.5.0) (2025-02-07)
### Features
* add api key delete ([#2210](https://github.com/homarr-labs/homarr/issues/2210)) ([8058b62](https://github.com/homarr-labs/homarr/commit/8058b6207ad4be1d646182b67b810c397097c054))
* custom time/date format in clock widget ([#2180](https://github.com/homarr-labs/homarr/issues/2180)) ([be603ba](https://github.com/homarr-labs/homarr/commit/be603badfed63ad7583a5265bef3b9e85f6b6b61))
* improve consistency and design ([#1867](https://github.com/homarr-labs/homarr/issues/1867)) ([5f36d8b](https://github.com/homarr-labs/homarr/commit/5f36d8b12528fd2c8c3da783b266d614203ec46a))
* open all apps in category ([#2212](https://github.com/homarr-labs/homarr/issues/2212)) ([bb67bf5](https://github.com/homarr-labs/homarr/commit/bb67bf5fa2968d91ad3522ebae960a6880f85987))
### Bug Fixes
* **deps:** update dependency @tabler/icons-react to ^3.30.0 ([#2266](https://github.com/homarr-labs/homarr/issues/2266)) ([c849109](https://github.com/homarr-labs/homarr/commit/c849109df5b622aca8cf7b392b94685581bf6b32))
* **deps:** update dependency drizzle-orm to ^0.39.2 ([#2255](https://github.com/homarr-labs/homarr/issues/2255)) ([3d0b319](https://github.com/homarr-labs/homarr/commit/3d0b319a88bdb8d955d7942553d92988bf5b2c28))
* **deps:** update dependency ioredis to v5.5.0 ([#2264](https://github.com/homarr-labs/homarr/issues/2264)) ([0da5a1b](https://github.com/homarr-labs/homarr/commit/0da5a1b0fb86ff2074f12a5e028fcc09ff94a4a5))
* **deps:** update dependency jotai to ^2.11.3 ([#2215](https://github.com/homarr-labs/homarr/issues/2215)) ([a385337](https://github.com/homarr-labs/homarr/commit/a3853372687aed543fad2dd89bfb13871af0f2c1))
* **deps:** update dependency octokit to ^4.1.1 ([#2262](https://github.com/homarr-labs/homarr/issues/2262)) ([3cbb656](https://github.com/homarr-labs/homarr/commit/3cbb656ee9b540d2dd4c7db37946efacbbfdc522))
* **deps:** update dependency sass to ^1.84.0 ([#2257](https://github.com/homarr-labs/homarr/issues/2257)) ([7bbbaf3](https://github.com/homarr-labs/homarr/commit/7bbbaf343befaaa3d03a91e932f683c5aec4b41e))
* **deps:** update dependency semver-parser to ^4.1.8 ([#2216](https://github.com/homarr-labs/homarr/issues/2216)) ([46cfb23](https://github.com/homarr-labs/homarr/commit/46cfb2383b6628472d3972da2166884330bf6d24))
* **deps:** update dependency trpc-to-openapi to ^2.1.3 ([#2244](https://github.com/homarr-labs/homarr/issues/2244)) ([c623a42](https://github.com/homarr-labs/homarr/commit/c623a420cadce677e39b99fceeb3f3a391f879ea))
* **deps:** update dependency typescript-eslint to ^8.23.0 ([#2232](https://github.com/homarr-labs/homarr/issues/2232)) ([83044d2](https://github.com/homarr-labs/homarr/commit/83044d23dd7accb1b3a21cdde1e034969a494ccb))
* **deps:** update testcontainers-node monorepo to ^10.18.0 ([#2259](https://github.com/homarr-labs/homarr/issues/2259)) ([7a8e671](https://github.com/homarr-labs/homarr/commit/7a8e67120576b333bfcf73171b54ccfeeda0d44a))
* **deps:** update turbo monorepo to ^2.4.0 ([#2207](https://github.com/homarr-labs/homarr/issues/2207)) ([6ce7bc0](https://github.com/homarr-labs/homarr/commit/6ce7bc0ee2dcd5150d72fdbc2070c05ba836ebd5))
* foreign key reference default search engine ([#2211](https://github.com/homarr-labs/homarr/issues/2211)) ([26468a4](https://github.com/homarr-labs/homarr/commit/26468a413795afa7cc3444058bfc73435fb7f2a0))
* make system health temp optional ([#2247](https://github.com/homarr-labs/homarr/issues/2247)) ([da09826](https://github.com/homarr-labs/homarr/commit/da098263930c9c9611a1505d52d3816bd2a49e7e))
## [1.4.0](https://github.com/homarr-labs/homarr/compare/v1.3.1...v1.4.0) (2025-01-31)
### Features
* weather widget - add wind speed, option to disable decimals for temperature & redesign dropdown forecast ([#2099](https://github.com/homarr-labs/homarr/issues/2099)) ([c19a400](https://github.com/homarr-labs/homarr/commit/c19a4009f37ec2aba8d4edd2d19819605ecb9575))
### Bug Fixes
* board crashes with empty string favicon ([#2192](https://github.com/homarr-labs/homarr/issues/2192)) ([bd0b7c9](https://github.com/homarr-labs/homarr/commit/bd0b7c9518af88e0a329ea6286cb5ef97098782a))
* **deps:** update dependency drizzle-kit to ^0.30.4 ([#2183](https://github.com/homarr-labs/homarr/issues/2183)) ([37ccfab](https://github.com/homarr-labs/homarr/commit/37ccfab89a7843c65d6f354bef2b0537b20ea757))
* **deps:** update dependency drizzle-orm to ^0.39.1 ([#2186](https://github.com/homarr-labs/homarr/issues/2186)) ([4c72a34](https://github.com/homarr-labs/homarr/commit/4c72a34daf06c5e00fc4ece6d990148c1cd6d87f))
* **deps:** update dependency jotai to ^2.11.2 ([#2201](https://github.com/homarr-labs/homarr/issues/2201)) ([c287e99](https://github.com/homarr-labs/homarr/commit/c287e9965f008b6ac6f3bac56057b94c412a3779))
* **deps:** update tanstack-query monorepo to ^5.66.0 ([#2202](https://github.com/homarr-labs/homarr/issues/2202)) ([e44c3a7](https://github.com/homarr-labs/homarr/commit/e44c3a7bc8be5b67d78096d5daa53ad23ccdea5d))
* **deps:** update tiptap monorepo to v2.11.4 ([#2195](https://github.com/homarr-labs/homarr/issues/2195)) ([c05c518](https://github.com/homarr-labs/homarr/commit/c05c51856baff5222b0abda56e1c11d9bc9bd6f9))
* **deps:** update tiptap monorepo to v2.11.5 ([#2203](https://github.com/homarr-labs/homarr/issues/2203)) ([9662e1a](https://github.com/homarr-labs/homarr/commit/9662e1abba3e105fd549d25ee0a0efc48cf293a1))
* **docker:** missing production env variable ([#2188](https://github.com/homarr-labs/homarr/issues/2188)) ([7774627](https://github.com/homarr-labs/homarr/commit/77746278a36f2b9d39c780eada25195121cc0803))
* negative curve radius system health ([#2197](https://github.com/homarr-labs/homarr/issues/2197)) ([c75d4f0](https://github.com/homarr-labs/homarr/commit/c75d4f0f58136a0499a51494eb54c78c745f0b3f))
* respect certificates for app ping ([#2198](https://github.com/homarr-labs/homarr/issues/2198)) ([d4154d2](https://github.com/homarr-labs/homarr/commit/d4154d259fa50bfd754e7ea98b872df78864e014))
* use hostname for suspense query url ([#2187](https://github.com/homarr-labs/homarr/issues/2187)) ([96fd5cc](https://github.com/homarr-labs/homarr/commit/96fd5cc35e9ca85aba48670860e6bf2a4a2caaa9))
## [1.3.1](https://github.com/homarr-labs/homarr/compare/v1.3.0...v1.3.1) (2025-01-28)
### Bug Fixes
* app url line clamp ([#2170](https://github.com/homarr-labs/homarr/issues/2170)) ([780efab](https://github.com/homarr-labs/homarr/commit/780efab80cdc5446a6468d60afe2c1d9ecca0f1e))
* automatic search engine creation ([#2172](https://github.com/homarr-labs/homarr/issues/2172)) ([8dd7fe2](https://github.com/homarr-labs/homarr/commit/8dd7fe297c431bbe448bc47b381370ade7c4d3d9))
* **deps:** update dependency swagger-ui-react to ^5.18.3 ([#2164](https://github.com/homarr-labs/homarr/issues/2164)) ([a4e9abc](https://github.com/homarr-labs/homarr/commit/a4e9abcda192a96754078eece4343c2c1c9fa032))
* **deps:** update dependency typescript-eslint to ^8.22.0 ([#2149](https://github.com/homarr-labs/homarr/issues/2149)) ([9d4aea2](https://github.com/homarr-labs/homarr/commit/9d4aea2c4187ddda6a0484d0f6a0403c04961dde))
* **deps:** update tanstack-query monorepo to ^5.65.1 ([#2161](https://github.com/homarr-labs/homarr/issues/2161)) ([55b1afb](https://github.com/homarr-labs/homarr/commit/55b1afbb2167ccb6f5115cbb1291dfe561a5aa05))
## [1.3.0](https://github.com/homarr-labs/homarr/compare/v1.2.0...v1.3.0) (2025-01-27) ## [1.3.0](https://github.com/homarr-labs/homarr/compare/v1.2.0...v1.3.0) (2025-01-27)
### Features ### Features
+6 -1
View File
@@ -10,6 +10,8 @@ RUN apk add --no-cache libc6-compat curl bash
RUN apk update RUN apk update
COPY . . COPY . .
# Install working version of corepack (See https://github.com/nodejs/corepack/issues/612)
RUN npm install -g corepack@0.31.0 && corepack --version
RUN corepack enable pnpm && pnpm install --recursive --frozen-lockfile RUN corepack enable pnpm && pnpm install --recursive --frozen-lockfile
# Copy static data as it is not part of the build # Copy static data as it is not part of the build
@@ -17,6 +19,8 @@ COPY static-data ./static-data
ARG SKIP_ENV_VALIDATION='true' ARG SKIP_ENV_VALIDATION='true'
ARG CI='true' ARG CI='true'
ARG DISABLE_REDIS_LOGS='true' ARG DISABLE_REDIS_LOGS='true'
# Install working version of corepack (See https://github.com/nodejs/corepack/issues/612)
RUN npm install -g corepack@0.31.0 && corepack --version
RUN corepack enable pnpm && pnpm build RUN corepack enable pnpm && pnpm build
FROM base AS runner FROM base AS runner
@@ -63,6 +67,7 @@ ENV DB_URL='/appdata/db/db.sqlite'
ENV DB_DIALECT='sqlite' ENV DB_DIALECT='sqlite'
ENV DB_DRIVER='better-sqlite3' ENV DB_DRIVER='better-sqlite3'
ENV AUTH_PROVIDERS='credentials' ENV AUTH_PROVIDERS='credentials'
ENV NODE_ENV='production'
ENTRYPOINT [ "/app/entrypoint.sh" ] ENTRYPOINT [ "/app/entrypoint.sh" ]
CMD ["sh", "run.sh"] CMD ["sh", "run.sh"]
+10 -10
View File
@@ -50,10 +50,10 @@
"@mantine/tiptap": "^7.16.2", "@mantine/tiptap": "^7.16.2",
"@million/lint": "1.0.14", "@million/lint": "1.0.14",
"@t3-oss/env-nextjs": "^0.12.0", "@t3-oss/env-nextjs": "^0.12.0",
"@tabler/icons-react": "^3.29.0", "@tabler/icons-react": "^3.30.0",
"@tanstack/react-query": "^5.65.0", "@tanstack/react-query": "^5.66.0",
"@tanstack/react-query-devtools": "^5.65.0", "@tanstack/react-query-devtools": "^5.66.0",
"@tanstack/react-query-next-experimental": "^5.65.0", "@tanstack/react-query-next-experimental": "^5.66.0",
"@trpc/client": "next", "@trpc/client": "next",
"@trpc/next": "next", "@trpc/next": "next",
"@trpc/react-query": "next", "@trpc/react-query": "next",
@@ -67,7 +67,7 @@
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"flag-icons": "^7.3.2", "flag-icons": "^7.3.2",
"glob": "^11.0.1", "glob": "^11.0.1",
"jotai": "^2.11.1", "jotai": "^2.11.3",
"mantine-react-table": "2.0.0-beta.8", "mantine-react-table": "2.0.0-beta.8",
"next": "15.1.6", "next": "15.1.6",
"postcss-preset-mantine": "^1.17.0", "postcss-preset-mantine": "^1.17.0",
@@ -76,9 +76,9 @@
"react-dom": "19.0.0", "react-dom": "19.0.0",
"react-error-boundary": "^5.0.0", "react-error-boundary": "^5.0.0",
"react-simple-code-editor": "^0.14.1", "react-simple-code-editor": "^0.14.1",
"sass": "^1.83.4", "sass": "^1.84.0",
"superjson": "2.2.2", "superjson": "2.2.2",
"swagger-ui-react": "^5.18.2", "swagger-ui-react": "^5.18.3",
"use-deep-compare-effect": "^1.8.1", "use-deep-compare-effect": "^1.8.1",
"zod": "^3.24.1" "zod": "^3.24.1"
}, },
@@ -86,12 +86,12 @@
"@homarr/eslint-config": "workspace:^0.2.0", "@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0", "@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0",
"@types/chroma-js": "3.1.0", "@types/chroma-js": "3.1.1",
"@types/node": "^22.10.10", "@types/node": "^22.13.1",
"@types/prismjs": "^1.26.5", "@types/prismjs": "^1.26.5",
"@types/react": "19.0.8", "@types/react": "19.0.8",
"@types/react-dom": "19.0.3", "@types/react-dom": "19.0.3",
"@types/swagger-ui-react": "^4.19.0", "@types/swagger-ui-react": "^5.18.0",
"concurrently": "^9.1.2", "concurrently": "^9.1.2",
"eslint": "^9.19.0", "eslint": "^9.19.0",
"node-loader": "^2.1.0", "node-loader": "^2.1.0",
@@ -18,8 +18,8 @@ import superjson from "superjson";
import type { SuperJSONResult } from "superjson"; import type { SuperJSONResult } from "superjson";
import type { AppRouter } from "@homarr/api"; import type { AppRouter } from "@homarr/api";
import { clientApi, getTrpcUrl } from "@homarr/api/client"; import { clientApi } from "@homarr/api/client";
import { createHeadersCallbackForSource } from "@homarr/api/shared"; import { createHeadersCallbackForSource, getTrpcUrl } from "@homarr/api/shared";
import { env } from "~/env"; import { env } from "~/env";
@@ -7,6 +7,7 @@ import "~/styles/gridstack.scss";
import { IntegrationProvider } from "@homarr/auth/client"; import { IntegrationProvider } from "@homarr/auth/client";
import { auth } from "@homarr/auth/next"; import { auth } from "@homarr/auth/next";
import { getIntegrationsWithPermissionsAsync } from "@homarr/auth/server"; import { getIntegrationsWithPermissionsAsync } from "@homarr/auth/server";
import { isNullOrWhitespace } from "@homarr/common";
import { getI18n } from "@homarr/translation/server"; import { getI18n } from "@homarr/translation/server";
import { createMetaTitle } from "~/metadata"; import { createMetaTitle } from "~/metadata";
@@ -48,11 +49,13 @@ export const createBoardContentPage = <TParams extends Record<string, unknown>>(
return { return {
title: board.metaTitle ?? createMetaTitle(t("board.content.metaTitle", { boardName: board.name })), title: board.metaTitle ?? createMetaTitle(t("board.content.metaTitle", { boardName: board.name })),
icons: { icons: {
icon: board.faviconImageUrl ? board.faviconImageUrl : undefined, icon: !isNullOrWhitespace(board.faviconImageUrl) ? board.faviconImageUrl : undefined,
apple: board.faviconImageUrl ? board.faviconImageUrl : undefined, apple: !isNullOrWhitespace(board.faviconImageUrl) ? board.faviconImageUrl : undefined,
}, },
appleWebApp: { appleWebApp: {
startupImage: { url: board.faviconImageUrl ? board.faviconImageUrl : "/logo/logo.png" }, startupImage: {
url: !isNullOrWhitespace(board.faviconImageUrl) ? board.faviconImageUrl : "/logo/logo.png",
},
}, },
}; };
} catch (error) { } catch (error) {
@@ -1,5 +1,5 @@
.bannerContainer { .bannerContainer {
border-radius: 8px; border-radius: 16px;
overflow: hidden; overflow: hidden;
@mixin dark { @mixin dark {
background: linear-gradient( background: linear-gradient(
@@ -1,11 +1,11 @@
"use client"; "use client";
import { useRef } from "react";
import Link from "next/link"; import Link from "next/link";
import { Button, Group, Stack, Textarea, TextInput } from "@mantine/core"; import { Button, Group, Stack, Textarea, TextInput } from "@mantine/core";
import type { z } from "zod"; import type { z } from "zod";
import { useZodForm } from "@homarr/form"; import { useZodForm } from "@homarr/form";
import type { TranslationFunction } from "@homarr/translation";
import { useI18n } from "@homarr/translation/client"; import { useI18n } from "@homarr/translation/client";
import { validation } from "@homarr/validation"; import { validation } from "@homarr/validation";
@@ -14,14 +14,21 @@ import { IconPicker } from "~/components/icons/picker/icon-picker";
type FormType = z.infer<typeof validation.app.manage>; type FormType = z.infer<typeof validation.app.manage>;
interface AppFormProps { interface AppFormProps {
submitButtonTranslation: (t: TranslationFunction) => string; buttonLabels: {
submit: string;
submitAndCreateAnother?: string;
};
initialValues?: FormType; initialValues?: FormType;
handleSubmit: (values: FormType) => void; handleSubmit: (values: FormType, redirect: boolean, afterSuccess?: () => void) => void;
isPending: boolean; isPending: boolean;
} }
export const AppForm = (props: AppFormProps) => { export const AppForm = ({
const { submitButtonTranslation, handleSubmit, initialValues, isPending } = props; buttonLabels,
handleSubmit: originalHandleSubmit,
initialValues,
isPending,
}: AppFormProps) => {
const t = useI18n(); const t = useI18n();
const form = useZodForm(validation.app.manage, { const form = useZodForm(validation.app.manage, {
@@ -33,11 +40,23 @@ export const AppForm = (props: AppFormProps) => {
}, },
}); });
const shouldCreateAnother = useRef(false);
const handleSubmit = (values: FormType) => {
const redirect = !shouldCreateAnother.current;
const afterSuccess = shouldCreateAnother.current
? () => {
form.reset();
shouldCreateAnother.current = false;
}
: undefined;
originalHandleSubmit(values, redirect, afterSuccess);
};
return ( return (
<form onSubmit={form.onSubmit(handleSubmit)}> <form onSubmit={form.onSubmit(handleSubmit)}>
<Stack> <Stack>
<TextInput {...form.getInputProps("name")} withAsterisk label={t("app.field.name.label")} /> <TextInput {...form.getInputProps("name")} withAsterisk label={t("app.field.name.label")} />
<IconPicker initialValue={initialValues?.iconUrl} {...form.getInputProps("iconUrl")} /> <IconPicker {...form.getInputProps("iconUrl")} />
<Textarea {...form.getInputProps("description")} label={t("app.field.description.label")} /> <Textarea {...form.getInputProps("description")} label={t("app.field.description.label")} />
<TextInput {...form.getInputProps("href")} label={t("app.field.url.label")} /> <TextInput {...form.getInputProps("href")} label={t("app.field.url.label")} />
@@ -45,8 +64,19 @@ export const AppForm = (props: AppFormProps) => {
<Button variant="default" component={Link} href="/manage/apps"> <Button variant="default" component={Link} href="/manage/apps">
{t("common.action.backToOverview")} {t("common.action.backToOverview")}
</Button> </Button>
{buttonLabels.submitAndCreateAnother && (
<Button
type="submit"
onClick={() => {
shouldCreateAnother.current = true;
}}
loading={isPending}
>
{buttonLabels.submitAndCreateAnother}
</Button>
)}
<Button type="submit" loading={isPending}> <Button type="submit" loading={isPending}>
{submitButtonTranslation(t)} {buttonLabels.submit}
</Button> </Button>
</Group> </Group>
</Stack> </Stack>
@@ -8,8 +8,7 @@ import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client"; import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client"; import { revalidatePathActionAsync } from "@homarr/common/client";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import type { TranslationFunction } from "@homarr/translation"; import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { useScopedI18n } from "@homarr/translation/client";
import type { validation } from "@homarr/validation"; import type { validation } from "@homarr/validation";
import { AppForm } from "../../_form"; import { AppForm } from "../../_form";
@@ -19,14 +18,15 @@ interface AppEditFormProps {
} }
export const AppEditForm = ({ app }: AppEditFormProps) => { export const AppEditForm = ({ app }: AppEditFormProps) => {
const t = useScopedI18n("app.page.edit.notification"); const tScoped = useScopedI18n("app.page.edit.notification");
const t = useI18n();
const router = useRouter(); const router = useRouter();
const { mutate, isPending } = clientApi.app.update.useMutation({ const { mutate, isPending } = clientApi.app.update.useMutation({
onSuccess: () => { onSuccess: () => {
showSuccessNotification({ showSuccessNotification({
title: t("success.title"), title: tScoped("success.title"),
message: t("success.message"), message: tScoped("success.message"),
}); });
void revalidatePathActionAsync("/manage/apps").then(() => { void revalidatePathActionAsync("/manage/apps").then(() => {
router.push("/manage/apps"); router.push("/manage/apps");
@@ -34,8 +34,8 @@ export const AppEditForm = ({ app }: AppEditFormProps) => {
}, },
onError: () => { onError: () => {
showErrorNotification({ showErrorNotification({
title: t("error.title"), title: tScoped("error.title"),
message: t("error.message"), message: tScoped("error.message"),
}); });
}, },
}); });
@@ -50,11 +50,11 @@ export const AppEditForm = ({ app }: AppEditFormProps) => {
[mutate, app.id], [mutate, app.id],
); );
const submitButtonTranslation = useCallback((t: TranslationFunction) => t("common.action.save"), []);
return ( return (
<AppForm <AppForm
submitButtonTranslation={submitButtonTranslation} buttonLabels={{
submit: t("common.action.save"),
}}
initialValues={app} initialValues={app}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
isPending={isPending} isPending={isPending}
@@ -7,44 +7,55 @@ import type { z } from "zod";
import { clientApi } from "@homarr/api/client"; import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client"; import { revalidatePathActionAsync } from "@homarr/common/client";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import type { TranslationFunction } from "@homarr/translation"; import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { useScopedI18n } from "@homarr/translation/client";
import type { validation } from "@homarr/validation"; import type { validation } from "@homarr/validation";
import { AppForm } from "../_form"; import { AppForm } from "../_form";
export const AppNewForm = () => { export const AppNewForm = () => {
const t = useScopedI18n("app.page.create.notification"); const tScoped = useScopedI18n("app.page.create.notification");
const t = useI18n();
const router = useRouter(); const router = useRouter();
const { mutate, isPending } = clientApi.app.create.useMutation({ const { mutate, isPending } = clientApi.app.create.useMutation({
onSuccess: () => {
showSuccessNotification({
title: t("success.title"),
message: t("success.message"),
});
void revalidatePathActionAsync("/manage/apps").then(() => {
router.push("/manage/apps");
});
},
onError: () => { onError: () => {
showErrorNotification({ showErrorNotification({
title: t("error.title"), title: tScoped("error.title"),
message: t("error.message"), message: tScoped("error.message"),
}); });
}, },
}); });
const handleSubmit = useCallback( const handleSubmit = useCallback(
(values: z.infer<typeof validation.app.manage>) => { (values: z.infer<typeof validation.app.manage>, redirect: boolean, afterSuccess?: () => void) => {
mutate(values); mutate(values, {
}, onSuccess() {
[mutate], showSuccessNotification({
); title: tScoped("success.title"),
message: tScoped("success.message"),
});
afterSuccess?.();
const submitButtonTranslation = useCallback((t: TranslationFunction) => t("common.action.create"), []); if (!redirect) {
return;
}
void revalidatePathActionAsync("/manage/apps").then(() => {
router.push("/manage/apps");
});
},
});
},
[mutate, router, tScoped],
);
return ( return (
<AppForm submitButtonTranslation={submitButtonTranslation} handleSubmit={handleSubmit} isPending={isPending} /> <AppForm
buttonLabels={{
submit: t("common.action.create"),
submitAndCreateAnother: t("common.action.createAnother"),
}}
handleSubmit={handleSubmit}
isPending={isPending}
/>
); );
}; };
@@ -45,7 +45,7 @@ export default async function AppsPage(props: AppsPageProps) {
<Stack> <Stack>
<Title>{t("page.list.title")}</Title> <Title>{t("page.list.title")}</Title>
<Group justify="space-between" align="center"> <Group justify="space-between" align="center">
<SearchInput placeholder={`${t("search")}...`} defaultValue={searchParams.search} /> <SearchInput placeholder={`${t("search")}...`} defaultValue={searchParams.search} flexExpand />
{session.user.permissions.includes("app-create") && ( {session.user.permissions.includes("app-create") && (
<MobileAffixButton component={Link} href="/manage/apps/new"> <MobileAffixButton component={Link} href="/manage/apps/new">
{t("page.create.title")} {t("page.create.title")}
@@ -80,7 +80,7 @@ const AppCard = async ({ app }: AppCardProps) => {
return ( return (
<Card withBorder> <Card withBorder>
<Group justify="space-between" wrap="nowrap"> <Group justify="space-between" wrap="nowrap">
<Group align="top" justify="start" wrap="nowrap"> <Group align="top" justify="start" wrap="nowrap" style={{ flex: "1" }}>
<Avatar <Avatar
size="sm" size="sm"
src={app.iconUrl} src={app.iconUrl}
@@ -91,7 +91,7 @@ const AppCard = async ({ app }: AppCardProps) => {
}, },
}} }}
/> />
<Stack gap={0}> <Stack gap={0} style={{ flex: "1" }}>
<Text fw={500} lineClamp={1}> <Text fw={500} lineClamp={1}>
{app.name} {app.name}
</Text> </Text>
@@ -101,7 +101,7 @@ const AppCard = async ({ app }: AppCardProps) => {
</Text> </Text>
)} )}
{app.href && ( {app.href && (
<Anchor href={app.href} lineClamp={1} size="sm" w="min-content"> <Anchor href={app.href} lineClamp={1} size="sm" style={{ wordBreak: "break-all" }}>
{app.href} {app.href}
</Anchor> </Anchor>
)} )}
@@ -67,7 +67,7 @@ const BoardCard = async ({ board }: BoardCardProps) => {
const VisibilityIcon = board.isPublic ? IconWorld : IconLock; const VisibilityIcon = board.isPublic ? IconWorld : IconLock;
return ( return (
<Card withBorder> <Card radius="lg" withBorder>
<CardSection p="sm" withBorder> <CardSection p="sm" withBorder>
<Group justify="space-between" align="center"> <Group justify="space-between" align="center">
<Group gap="sm"> <Group gap="sm">
@@ -106,15 +106,25 @@ const BoardCard = async ({ board }: BoardCardProps) => {
</Group> </Group>
</CardSection> </CardSection>
<CardSection p="sm"> <CardSection>
<Group wrap="nowrap"> <Group gap={0} wrap="nowrap">
<Button component={Link} href={`/boards/${board.name}`} variant="default" fullWidth> <Button
style={{ border: "none", borderRadius: 0 }}
component={Link}
href={`/boards/${board.name}`}
variant="default"
fullWidth
>
{t("action.open.label")} {t("action.open.label")}
</Button> </Button>
{isMenuVisible && ( {isMenuVisible && (
<Menu position="bottom-end"> <Menu position="bottom-end">
<MenuTarget> <MenuTarget>
<ActionIcon variant="default" size="lg"> <ActionIcon
style={{ borderTop: "none", borderBottom: "none", borderRight: "none", borderRadius: 0 }}
variant="default"
size="lg"
>
<IconDotsVertical size={16} stroke={1.5} /> <IconDotsVertical size={16} stroke={1.5} />
</ActionIcon> </ActionIcon>
</MenuTarget> </MenuTarget>
@@ -70,9 +70,9 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
if (testConnectionError) { if (testConnectionError) {
showErrorNotification({ showErrorNotification({
title: t(`integration.testConnection.notification.${testConnectionError.key}.title`), title: t(`integration.testConnection.notification.${testConnectionError.key}.title`),
message: testConnectionError.message message:
? testConnectionError.message testConnectionError.message ??
: t(`integration.testConnection.notification.${testConnectionError.key}.message`), t(`integration.testConnection.notification.${testConnectionError.key}.message`),
}); });
return; return;
} }
@@ -33,6 +33,7 @@ export const IntegrationCreateDropdownContent = () => {
value={search} value={search}
data-autofocus data-autofocus
onChange={handleSearch} onChange={handleSearch}
variant="filled"
/> />
{filteredKinds.length > 0 ? ( {filteredKinds.length > 0 ? (
@@ -63,9 +63,9 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
if (testConnectionError) { if (testConnectionError) {
showErrorNotification({ showErrorNotification({
title: t(`integration.testConnection.notification.${testConnectionError.key}.title`), title: t(`integration.testConnection.notification.${testConnectionError.key}.title`),
message: testConnectionError.message message:
? testConnectionError.message testConnectionError.message ??
: t(`integration.testConnection.notification.${testConnectionError.key}.message`), t(`integration.testConnection.notification.${testConnectionError.key}.message`),
}); });
return; return;
} }
@@ -0,0 +1,36 @@
.root {
border-radius: var(--mantine-radius-lg);
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
}
.item {
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
border: 1px solid transparent;
position: relative;
z-index: 0;
transition: transform 150ms ease;
overflow: hidden;
&[data-first="true"] {
border-radius: var(--mantine-radius-lg) var(--mantine-radius-lg) 0 0;
}
&[data-last="true"] {
border-radius: 0 0 var(--mantine-radius-lg) var(--mantine-radius-lg);
}
&[data-active] {
transform: scale(1.01);
z-index: 1;
background-color: var(--mantine-color-body);
border-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));
box-shadow: var(--mantine-shadow-md);
border-radius: var(--mantine-radius-lg);
}
}
.chevron {
&[data-rotate] {
transform: rotate(180deg);
}
}
@@ -44,6 +44,7 @@ import { NoResults } from "~/components/no-results";
import { ActiveTabAccordion } from "../../../../components/active-tab-accordion"; import { ActiveTabAccordion } from "../../../../components/active-tab-accordion";
import { DeleteIntegrationActionButton } from "./_integration-buttons"; import { DeleteIntegrationActionButton } from "./_integration-buttons";
import { IntegrationCreateDropdownContent } from "./new/_integration-new-dropdown"; import { IntegrationCreateDropdownContent } from "./new/_integration-new-dropdown";
import classes from "./page.module.css";
interface IntegrationsPageProps { interface IntegrationsPageProps {
searchParams: Promise<{ searchParams: Promise<{
@@ -133,7 +134,7 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
return <NoResults icon={IconPlugX} title={t("page.list.noResults.title")} />; return <NoResults icon={IconPlugX} title={t("page.list.noResults.title")} />;
} }
const grouppedIntegrations = integrations.reduce( const groupedIntegrations = integrations.reduce(
(acc, integration) => { (acc, integration) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!acc[integration.kind]) { if (!acc[integration.kind]) {
@@ -147,11 +148,13 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
{} as Record<IntegrationKind, RouterOutputs["integration"]["all"]>, {} as Record<IntegrationKind, RouterOutputs["integration"]["all"]>,
); );
const entries = objectEntries(groupedIntegrations);
return ( return (
<ActiveTabAccordion defaultValue={activeTab} variant="separated"> <ActiveTabAccordion defaultValue={activeTab} radius="lg" classNames={classes}>
{objectEntries(grouppedIntegrations).map(([kind, integrations]) => ( {entries.map(([kind, integrations], index) => (
<AccordionItem key={kind} value={kind}> <AccordionItem key={kind} value={kind} data-first={index === 0} data-last={index === entries.length - 1}>
<AccordionControl icon={<IntegrationAvatar size="sm" kind={kind} />}> <AccordionControl icon={<IntegrationAvatar size="sm" kind={kind} radius="sm" />}>
<Group> <Group>
<Text>{getIntegrationName(kind)}</Text> <Text>{getIntegrationName(kind)}</Text>
<CountBadge count={integrations.length} /> <CountBadge count={integrations.length} />
+30 -23
View File
@@ -1,26 +1,26 @@
import type { PropsWithChildren } from "react"; import type { PropsWithChildren } from "react";
import { AppShellMain } from "@mantine/core"; import { AppShellMain } from "@mantine/core";
import { import {
IconAffiliateFilled,
IconBook2, IconBook2,
IconBox, IconBox,
IconBrandDiscord, IconBrandDiscord,
IconBrandDocker, IconBrandDocker,
IconBrandGithub, IconBrandGithub,
IconBrandTablerFilled,
IconCertificate, IconCertificate,
IconClipboardListFilled,
IconDirectionsFilled,
IconGitFork, IconGitFork,
IconHome, IconHelpSquareRoundedFilled,
IconInfoSmall, IconHomeFilled,
IconLayoutDashboard, IconLayoutDashboardFilled,
IconLogs,
IconMailForward, IconMailForward,
IconPhoto, IconPhotoFilled,
IconPlug, IconPointerFilled,
IconQuestionMark,
IconReport,
IconSearch, IconSearch,
IconSettings, IconSettingsFilled,
IconTool, IconUserFilled,
IconUser,
IconUsers, IconUsers,
IconUsersGroup, IconUsersGroup,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
@@ -31,6 +31,7 @@ import { createDocumentationLink } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server"; import { getScopedI18n } from "@homarr/translation/server";
import { MainHeader } from "~/components/layout/header"; import { MainHeader } from "~/components/layout/header";
import { homarrLogoPath } from "~/components/layout/logo/homarr-logo";
import type { NavigationLink } from "~/components/layout/navigation"; import type { NavigationLink } from "~/components/layout/navigation";
import { MainNavigation } from "~/components/layout/navigation"; import { MainNavigation } from "~/components/layout/navigation";
import { ClientShell } from "~/components/layout/shell"; import { ClientShell } from "~/components/layout/shell";
@@ -41,11 +42,11 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
const navigationLinks: NavigationLink[] = [ const navigationLinks: NavigationLink[] = [
{ {
label: t("items.home"), label: t("items.home"),
icon: IconHome, icon: IconHomeFilled,
href: "/manage", href: "/manage",
}, },
{ {
icon: IconLayoutDashboard, icon: IconLayoutDashboardFilled,
href: "/manage/boards", href: "/manage/boards",
label: t("items.boards"), label: t("items.boards"),
}, },
@@ -54,9 +55,12 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
href: "/manage/apps", href: "/manage/apps",
label: t("items.apps"), label: t("items.apps"),
hidden: !session, hidden: !session,
iconProps: {
strokeWidth: 2.5,
},
}, },
{ {
icon: IconPlug, icon: IconAffiliateFilled,
href: "/manage/integrations", href: "/manage/integrations",
label: t("items.integrations"), label: t("items.integrations"),
hidden: !session, hidden: !session,
@@ -66,15 +70,18 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
href: "/manage/search-engines", href: "/manage/search-engines",
label: t("items.searchEngies"), label: t("items.searchEngies"),
hidden: !session, hidden: !session,
iconProps: {
strokeWidth: 2.5,
},
}, },
{ {
icon: IconPhoto, icon: IconPhotoFilled,
href: "/manage/medias", href: "/manage/medias",
label: t("items.medias"), label: t("items.medias"),
hidden: !session, hidden: !session,
}, },
{ {
icon: IconUser, icon: IconUserFilled,
label: t("items.users.label"), label: t("items.users.label"),
hidden: !session?.user.permissions.includes("admin"), hidden: !session?.user.permissions.includes("admin"),
items: [ items: [
@@ -98,7 +105,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
}, },
{ {
label: t("items.tools.label"), label: t("items.tools.label"),
icon: IconTool, icon: IconPointerFilled,
// As permissions always include there children permissions, we can check other-view-logs as admin includes it // As permissions always include there children permissions, we can check other-view-logs as admin includes it
hidden: !session?.user.permissions.includes("other-view-logs"), hidden: !session?.user.permissions.includes("other-view-logs"),
items: [ items: [
@@ -110,13 +117,13 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
}, },
{ {
label: t("items.tools.items.api"), label: t("items.tools.items.api"),
icon: IconPlug, icon: IconDirectionsFilled,
href: "/manage/tools/api", href: "/manage/tools/api",
hidden: !session?.user.permissions.includes("admin"), hidden: !session?.user.permissions.includes("admin"),
}, },
{ {
label: t("items.tools.items.logs"), label: t("items.tools.items.logs"),
icon: IconLogs, icon: IconBrandTablerFilled,
href: "/manage/tools/logs", href: "/manage/tools/logs",
hidden: !session?.user.permissions.includes("other-view-logs"), hidden: !session?.user.permissions.includes("other-view-logs"),
}, },
@@ -128,7 +135,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
}, },
{ {
label: t("items.tools.items.tasks"), label: t("items.tools.items.tasks"),
icon: IconReport, icon: IconClipboardListFilled,
href: "/manage/tools/tasks", href: "/manage/tools/tasks",
hidden: !session?.user.permissions.includes("admin"), hidden: !session?.user.permissions.includes("admin"),
}, },
@@ -137,12 +144,12 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
{ {
label: t("items.settings"), label: t("items.settings"),
href: "/manage/settings", href: "/manage/settings",
icon: IconSettings, icon: IconSettingsFilled,
hidden: !session?.user.permissions.includes("admin"), hidden: !session?.user.permissions.includes("admin"),
}, },
{ {
label: t("items.help.label"), label: t("items.help.label"),
icon: IconQuestionMark, icon: IconHelpSquareRoundedFilled,
items: [ items: [
{ {
label: t("items.help.items.documentation"), label: t("items.help.items.documentation"),
@@ -172,7 +179,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
}, },
{ {
label: t("items.about"), label: t("items.about"),
icon: IconInfoSmall, icon: homarrLogoPath,
href: "/manage/about", href: "/manage/about",
}, },
]; ];
@@ -61,7 +61,7 @@ export default async function GroupsListPage(props: MediaListPageProps) {
const { items: medias, totalCount } = await api.media.getPaginated(searchParams); const { items: medias, totalCount } = await api.media.getPaginated(searchParams);
return ( return (
<ManageContainer size="xl"> <ManageContainer>
<DynamicBreadcrumb /> <DynamicBreadcrumb />
<Stack> <Stack>
<Title>{t("media.plural")}</Title> <Title>{t("media.plural")}</Title>
+1 -1
View File
@@ -83,7 +83,7 @@ export default async function ManagementPage() {
{links.map( {links.map(
(link) => (link) =>
!link.hidden && ( !link.hidden && (
<Card component={Link} href={link.href} key={link.href} withBorder> <Card component={Link} href={link.href} key={link.href} radius="lg">
<Group justify="space-between" wrap="nowrap"> <Group justify="space-between" wrap="nowrap">
<Group wrap="nowrap"> <Group wrap="nowrap">
<Text size="2.4rem" fw="bolder"> <Text size="2.4rem" fw="bolder">
@@ -58,7 +58,7 @@ export const SearchEngineForm = (props: SearchEngineFormProps) => {
/> />
</Grid.Col> </Grid.Col>
</Grid> </Grid>
<IconPicker initialValue={initialValues?.iconUrl} {...form.getInputProps("iconUrl")} /> <IconPicker {...form.getInputProps("iconUrl")} />
<Fieldset legend={t("search.engine.page.edit.configControl")}> <Fieldset legend={t("search.engine.page.edit.configControl")}>
<SegmentedControl <SegmentedControl
@@ -45,7 +45,7 @@ export default async function SearchEnginesPage(props: SearchEnginesPageProps) {
<Stack> <Stack>
<Title>{tEngine("page.list.title")}</Title> <Title>{tEngine("page.list.title")}</Title>
<Group justify="space-between" align="center"> <Group justify="space-between" align="center">
<SearchInput placeholder={`${tEngine("search")}...`} defaultValue={searchParams.search} /> <SearchInput placeholder={`${tEngine("search")}...`} defaultValue={searchParams.search} flexExpand />
{session.user.permissions.includes("search-engine-create") && ( {session.user.permissions.includes("search-engine-create") && (
<MobileAffixButton component={Link} href="/manage/search-engines/new"> <MobileAffixButton component={Link} href="/manage/search-engines/new">
{tEngine("page.create.title")} {tEngine("page.create.title")}
@@ -1,14 +1,15 @@
"use client"; "use client";
import { useMemo } from "react"; import { useCallback, useMemo } from "react";
import { Button, Group, Stack, Text, Title } from "@mantine/core"; import { ActionIcon, Button, Group, Stack, Text, Title } from "@mantine/core";
import { IconTrash } from "@tabler/icons-react";
import type { MRT_ColumnDef } from "mantine-react-table"; import type { MRT_ColumnDef } from "mantine-react-table";
import { MantineReactTable, useMantineReactTable } from "mantine-react-table"; import { MantineReactTable, useMantineReactTable } from "mantine-react-table";
import type { RouterOutputs } from "@homarr/api"; import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client"; import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client"; import { revalidatePathActionAsync } from "@homarr/common/client";
import { useModalAction } from "@homarr/modals"; import { useConfirmModal, useModalAction } from "@homarr/modals";
import { useScopedI18n } from "@homarr/translation/client"; import { useScopedI18n } from "@homarr/translation/client";
import { UserAvatar } from "@homarr/ui"; import { UserAvatar } from "@homarr/ui";
@@ -20,7 +21,8 @@ interface ApiKeysManagementProps {
export const ApiKeysManagement = ({ apiKeys }: ApiKeysManagementProps) => { export const ApiKeysManagement = ({ apiKeys }: ApiKeysManagementProps) => {
const { openModal } = useModalAction(CopyApiKeyModal); const { openModal } = useModalAction(CopyApiKeyModal);
const { mutate, isPending } = clientApi.apiKeys.create.useMutation({ const { openConfirmModal } = useConfirmModal();
const { mutate: mutateCreate, isPending: isPendingCreate } = clientApi.apiKeys.create.useMutation({
async onSuccess(data) { async onSuccess(data) {
openModal({ openModal({
apiKey: data.apiKey, apiKey: data.apiKey,
@@ -28,7 +30,26 @@ export const ApiKeysManagement = ({ apiKeys }: ApiKeysManagementProps) => {
await revalidatePathActionAsync("/manage/tools/api"); await revalidatePathActionAsync("/manage/tools/api");
}, },
}); });
const { mutateAsync: mutateDeleteAsync, isPending: isPendingDelete } = clientApi.apiKeys.delete.useMutation({
async onSuccess() {
await revalidatePathActionAsync("/manage/tools/api");
},
});
const t = useScopedI18n("management.page.tool.api.tab.apiKey"); const t = useScopedI18n("management.page.tool.api.tab.apiKey");
const handleDelete = useCallback(
(id: string) => {
openConfirmModal({
title: t("modal.delete.title"),
children: t("modal.delete.text"),
// eslint-disable-next-line no-restricted-syntax
async onConfirm() {
await mutateDeleteAsync({ apiKeyId: id });
},
});
},
[t, openConfirmModal, mutateDeleteAsync],
);
const columns = useMemo<MRT_ColumnDef<RouterOutputs["apiKeys"]["getAll"][number]>[]>( const columns = useMemo<MRT_ColumnDef<RouterOutputs["apiKeys"]["getAll"][number]>[]>(
() => [ () => [
@@ -46,8 +67,18 @@ export const ApiKeysManagement = ({ apiKeys }: ApiKeysManagementProps) => {
</Group> </Group>
), ),
}, },
{
header: t("table.header.actions"),
Cell: ({ row }) => (
<Group gap="xs">
<ActionIcon onClick={() => handleDelete(row.original.id)} loading={isPendingDelete} c="red">
<IconTrash size="1rem" />
</ActionIcon>
</Group>
),
},
], ],
[t], [t, handleDelete, isPendingDelete],
); );
const table = useMantineReactTable({ const table = useMantineReactTable({
@@ -56,9 +87,9 @@ export const ApiKeysManagement = ({ apiKeys }: ApiKeysManagementProps) => {
renderTopToolbarCustomActions: () => ( renderTopToolbarCustomActions: () => (
<Button <Button
onClick={() => { onClick={() => {
mutate(); mutateCreate();
}} }}
loading={isPending} loading={isPendingCreate}
> >
{t("button.createApiToken")} {t("button.createApiToken")}
</Button> </Button>
+2 -1
View File
@@ -1,6 +1,7 @@
import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter, createTRPCContext } from "@homarr/api"; import { appRouter, createTRPCContext } from "@homarr/api";
import { trpcPath } from "@homarr/api/shared";
import { auth } from "@homarr/auth/next"; import { auth } from "@homarr/auth/next";
import { logger } from "@homarr/log"; import { logger } from "@homarr/log";
@@ -25,7 +26,7 @@ export function OPTIONS() {
const handler = auth(async (req) => { const handler = auth(async (req) => {
const response = await fetchRequestHandler({ const response = await fetchRequestHandler({
endpoint: "/api/trpc", endpoint: trpcPath,
router: appRouter, router: appRouter,
req, req,
createContext: () => createTRPCContext({ session: req.auth, headers: req.headers }), createContext: () => createTRPCContext({ session: req.auth, headers: req.headers }),
@@ -1,5 +1,6 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { fetchApi } from "@homarr/api/client";
import { createId } from "@homarr/db/client"; import { createId } from "@homarr/db/client";
import { useConfirmModal, useModalAction } from "@homarr/modals"; import { useConfirmModal, useModalAction } from "@homarr/modals";
import { useI18n } from "@homarr/translation/client"; import { useI18n } from "@homarr/translation/client";
@@ -7,6 +8,7 @@ import { useI18n } from "@homarr/translation/client";
import type { CategorySection } from "~/app/[locale]/boards/_types"; import type { CategorySection } from "~/app/[locale]/boards/_types";
import { useCategoryActions } from "./category-actions"; import { useCategoryActions } from "./category-actions";
import { CategoryEditModal } from "./category-edit-modal"; import { CategoryEditModal } from "./category-edit-modal";
import { filterByItemKind } from "./filter";
export const useCategoryMenuActions = (category: CategorySection) => { export const useCategoryMenuActions = (category: CategorySection) => {
const { openModal } = useModalAction(CategoryEditModal); const { openModal } = useModalAction(CategoryEditModal);
@@ -97,6 +99,28 @@ export const useCategoryMenuActions = (category: CategorySection) => {
); );
}, [category, openModal, renameCategory, t]); }, [category, openModal, renameCategory, t]);
const openAllInNewTabs = useCallback(async () => {
const appIds = filterByItemKind(category.items, "app").map((item) => {
return item.options.appId;
});
const apps = await fetchApi.app.byIds.query(appIds);
const appsWithUrls = apps.filter((app) => app.href && app.href.length > 0);
for (const app of appsWithUrls) {
const openedWindow = window.open(app.href ?? undefined);
if (openedWindow) {
continue;
}
openConfirmModal({
title: t("section.category.openAllInNewTabs.title"),
children: t("section.category.openAllInNewTabs.text"),
});
break;
}
}, [category, t, openConfirmModal]);
return { return {
addCategoryAbove, addCategoryAbove,
addCategoryBelow, addCategoryBelow,
@@ -104,5 +128,6 @@ export const useCategoryMenuActions = (category: CategorySection) => {
moveCategoryDown, moveCategoryDown,
remove, remove,
edit, edit,
openAllInNewTabs,
}; };
}; };
@@ -5,6 +5,7 @@ import { ActionIcon, Menu } from "@mantine/core";
import { import {
IconDotsVertical, IconDotsVertical,
IconEdit, IconEdit,
IconExternalLink,
IconRowInsertBottom, IconRowInsertBottom,
IconRowInsertTop, IconRowInsertTop,
IconTransitionBottom, IconTransitionBottom,
@@ -12,6 +13,7 @@ import {
IconTrash, IconTrash,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import type { MaybePromise } from "@homarr/common/types";
import { useScopedI18n } from "@homarr/translation/client"; import { useScopedI18n } from "@homarr/translation/client";
import type { TablerIcon } from "@homarr/ui"; import type { TablerIcon } from "@homarr/ui";
@@ -27,8 +29,6 @@ export const CategoryMenu = ({ category }: Props) => {
const actions = useActions(category); const actions = useActions(category);
const t = useScopedI18n("section.category"); const t = useScopedI18n("section.category");
if (actions.length === 0) return null;
return ( return (
<Menu withArrow> <Menu withArrow>
<Menu.Target> <Menu.Target>
@@ -37,18 +37,20 @@ export const CategoryMenu = ({ category }: Props) => {
</ActionIcon> </ActionIcon>
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
{actions.map((action) => ( {actions.map((action) => {
<React.Fragment key={action.label}> return (
{"group" in action && <Menu.Label>{t(action.group)}</Menu.Label>} <React.Fragment key={action.label}>
<Menu.Item {"group" in action && <Menu.Label>{t(action.group)}</Menu.Label>}
leftSection={<action.icon size="1rem" />} <Menu.Item
onClick={action.onClick} leftSection={<action.icon size="1rem" />}
color={"color" in action ? action.color : undefined} onClick={action.onClick}
> color={"color" in action ? action.color : undefined}
{t(action.label)} >
</Menu.Item> {t(action.label)}
</React.Fragment> </Menu.Item>
))} </React.Fragment>
);
})}
</Menu.Dropdown> </Menu.Dropdown>
</Menu> </Menu>
); );
@@ -106,15 +108,21 @@ const useEditModeActions = (category: CategorySection) => {
] as const satisfies ActionDefinition[]; ] as const satisfies ActionDefinition[];
}; };
// TODO: once apps are added we can use this for the open many apps action const useNonEditModeActions = (category: CategorySection) => {
const useNonEditModeActions = (_category: CategorySection) => { const { openAllInNewTabs } = useCategoryMenuActions(category);
return [] as const satisfies ActionDefinition[]; return [
{
icon: IconExternalLink,
label: "action.openAllInNewTabs",
onClick: openAllInNewTabs,
},
] as const satisfies ActionDefinition[];
}; };
interface ActionDefinition { interface ActionDefinition {
icon: TablerIcon; icon: TablerIcon;
label: string; label: string;
onClick: () => void; onClick: () => MaybePromise<void>;
color?: string; color?: string;
group?: string; group?: string;
} }
@@ -0,0 +1,14 @@
import type { WidgetKind } from "@homarr/definitions";
import type { WidgetComponentProps } from "@homarr/widgets";
import { reduceWidgetOptionsWithDefaultValues } from "@homarr/widgets";
import type { Item } from "~/app/[locale]/boards/_types";
export const filterByItemKind = <TKind extends WidgetKind>(items: Item[], kind: TKind) => {
return items
.filter((item) => item.kind === kind)
.map((item) => ({
...item,
options: reduceWidgetOptionsWithDefaultValues(kind, item.options) as WidgetComponentProps<TKind>["options"],
}));
};
@@ -1,5 +1,5 @@
import type { FocusEventHandler } from "react"; import type { FocusEventHandler } from "react";
import { startTransition, useState } from "react"; import { startTransition } from "react";
import { import {
ActionIcon, ActionIcon,
Box, Box,
@@ -17,7 +17,7 @@ import {
UnstyledButton, UnstyledButton,
useCombobox, useCombobox,
} from "@mantine/core"; } from "@mantine/core";
import { useDebouncedValue } from "@mantine/hooks"; import { useDebouncedValue, useUncontrolled } from "@mantine/hooks";
import { IconUpload } from "@tabler/icons-react"; import { IconUpload } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client"; import { clientApi } from "@homarr/api/client";
@@ -28,17 +28,27 @@ import { UploadMedia } from "~/app/[locale]/manage/medias/_actions/upload-media"
import classes from "./icon-picker.module.css"; import classes from "./icon-picker.module.css";
interface IconPickerProps { interface IconPickerProps {
initialValue?: string; value?: string;
onChange: (iconUrl: string) => void; onChange: (iconUrl: string) => void;
error?: string | null; error?: string | null;
onFocus?: FocusEventHandler; onFocus?: FocusEventHandler;
onBlur?: FocusEventHandler; onBlur?: FocusEventHandler;
} }
export const IconPicker = ({ initialValue, onChange, error, onFocus, onBlur }: IconPickerProps) => { export const IconPicker = ({ value: propsValue, onChange, error, onFocus, onBlur }: IconPickerProps) => {
const [value, setValue] = useState<string>(initialValue ?? ""); const [value, setValue] = useUncontrolled({
const [search, setSearch] = useState(initialValue ?? ""); value: propsValue,
const [previewUrl, setPreviewUrl] = useState<string | null>(initialValue ?? null); onChange,
});
const [search, setSearch] = useUncontrolled({
value,
onChange: (value) => {
setValue(value);
},
});
const [previewUrl, setPreviewUrl] = useUncontrolled({
value: propsValue ?? null,
});
const { data: session } = useSession(); const { data: session } = useSession();
const tCommon = useScopedI18n("common"); const tCommon = useScopedI18n("common");
@@ -68,10 +78,9 @@ export const IconPicker = ({ initialValue, onChange, error, onFocus, onBlur }: I
onClick={() => { onClick={() => {
const value = item.url; const value = item.url;
startTransition(() => { startTransition(() => {
setValue(value);
setPreviewUrl(value); setPreviewUrl(value);
setSearch(value); setSearch(value);
onChange(value); setValue(value);
combobox.closeDropdown(); combobox.closeDropdown();
}); });
}} }}
@@ -128,7 +137,6 @@ export const IconPicker = ({ initialValue, onChange, error, onFocus, onBlur }: I
setSearch(event.currentTarget.value); setSearch(event.currentTarget.value);
setValue(event.currentTarget.value); setValue(event.currentTarget.value);
setPreviewUrl(null); setPreviewUrl(null);
onChange(event.currentTarget.value);
}} }}
onClick={() => combobox.openDropdown()} onClick={() => combobox.openDropdown()}
onFocus={(event) => { onFocus={(event) => {
@@ -154,7 +162,6 @@ export const IconPicker = ({ initialValue, onChange, error, onFocus, onBlur }: I
setValue(url); setValue(url);
setPreviewUrl(url); setPreviewUrl(url);
setSearch(url); setSearch(url);
onChange(url);
}); });
}} }}
> >
@@ -20,6 +20,7 @@ export const DesktopSearchInput = () => {
size="sm" size="sm"
leftSection={<IconSearch size={20} stroke={1.5} />} leftSection={<IconSearch size={20} stroke={1.5} />}
onClick={openSpotlight} onClick={openSpotlight}
radius="xl"
> >
{`${t("search.placeholder")}...`} {`${t("search.placeholder")}...`}
</TextInput> </TextInput>
@@ -1,7 +1,7 @@
import type { JSX } from "react"; import type { JSX } from "react";
import { AppShellNavbar, AppShellSection, ScrollArea } from "@mantine/core"; import { AppShellNavbar, AppShellSection, Image, ScrollArea } from "@mantine/core";
import type { TablerIcon } from "@homarr/ui"; import type { TablerIcon, TablerIconProps } from "@homarr/ui";
import type { ClientNavigationLink } from "./navigation-link"; import type { ClientNavigationLink } from "./navigation-link";
import { CommonNavLink } from "./navigation-link"; import { CommonNavLink } from "./navigation-link";
@@ -27,8 +27,13 @@ export const MainNavigation = ({ headerSection, footerSection, links }: MainNavi
return null; return null;
} }
const { icon: TablerIcon, ...props } = link; const { icon: TablerIcon, iconProps, ...props } = link;
const Icon = <TablerIcon size={20} stroke={1.5} />; const Icon =
typeof TablerIcon === "string" ? (
<Image src={TablerIcon} w={20} h={20} />
) : (
<TablerIcon size={20} stroke={1.5} {...iconProps} />
);
let clientLink: ClientNavigationLink; let clientLink: ClientNavigationLink;
if ("items" in props) { if ("items" in props) {
clientLink = { clientLink = {
@@ -38,7 +43,7 @@ export const MainNavigation = ({ headerSection, footerSection, links }: MainNavi
.map((item) => { .map((item) => {
return { return {
...item, ...item,
icon: <item.icon size={20} stroke={1.5} />, icon: <item.icon size={20} stroke={1.5} {...iconProps} />,
}; };
}), }),
} as ClientNavigationLink; } as ClientNavigationLink;
@@ -55,7 +60,8 @@ export const MainNavigation = ({ headerSection, footerSection, links }: MainNavi
interface CommonNavigationLinkProps { interface CommonNavigationLinkProps {
label: string; label: string;
icon: TablerIcon; icon: TablerIcon | string;
iconProps?: TablerIconProps;
hidden?: boolean; hidden?: boolean;
} }
+2 -2
View File
@@ -4,7 +4,7 @@ import { createTRPCClient, httpLink } from "@trpc/client";
import SuperJSON from "superjson"; import SuperJSON from "superjson";
import type { AppRouter } from "@homarr/api"; import type { AppRouter } from "@homarr/api";
import { createHeadersCallbackForSource } from "@homarr/api/shared"; import { createHeadersCallbackForSource, getTrpcUrl } from "@homarr/api/shared";
import { createI18nMiddleware } from "@homarr/translation/middleware"; import { createI18nMiddleware } from "@homarr/translation/middleware";
export async function middleware(request: NextRequest) { export async function middleware(request: NextRequest) {
@@ -34,7 +34,7 @@ export const config = {
export const serverFetchApi = createTRPCClient<AppRouter>({ export const serverFetchApi = createTRPCClient<AppRouter>({
links: [ links: [
httpLink({ httpLink({
url: `http://${process.env.HOSTNAME ?? "localhost"}:3000/api/trpc`, url: getTrpcUrl(),
transformer: SuperJSON, transformer: SuperJSON,
headers: createHeadersCallbackForSource("server-fetch"), headers: createHeadersCallbackForSource("server-fetch"),
}), }),
+1 -1
View File
@@ -44,7 +44,7 @@
"@homarr/eslint-config": "workspace:^0.2.0", "@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0", "@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0",
"@types/node": "^22.10.10", "@types/node": "^22.13.1",
"dotenv-cli": "^8.0.0", "dotenv-cli": "^8.0.0",
"eslint": "^9.19.0", "eslint": "^9.19.0",
"prettier": "^3.4.2", "prettier": "^3.4.2",
+8 -8
View File
@@ -1,6 +1,6 @@
{ {
"name": "homarr", "name": "homarr",
"version": "1.3.0", "version": "1.5.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "cross-env CI=true turbo build", "build": "cross-env CI=true turbo build",
@@ -38,22 +38,22 @@
"@semantic-release/github": "^11.0.1", "@semantic-release/github": "^11.0.1",
"@semantic-release/npm": "^12.0.1", "@semantic-release/npm": "^12.0.1",
"@semantic-release/release-notes-generator": "^14.0.3", "@semantic-release/release-notes-generator": "^14.0.3",
"@turbo/gen": "^2.3.4", "@turbo/gen": "^2.4.0",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"@vitest/coverage-v8": "^3.0.4", "@vitest/coverage-v8": "^3.0.5",
"@vitest/ui": "^3.0.4", "@vitest/ui": "^3.0.5",
"conventional-changelog-conventionalcommits": "^8.0.0", "conventional-changelog-conventionalcommits": "^8.0.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"jsdom": "^26.0.0", "jsdom": "^26.0.0",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"semantic-release": "^24.2.1", "semantic-release": "^24.2.1",
"testcontainers": "^10.17.2", "testcontainers": "^10.18.0",
"turbo": "^2.3.4", "turbo": "^2.4.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"vite-tsconfig-paths": "^5.1.4", "vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.4" "vitest": "^3.0.5"
}, },
"packageManager": "pnpm@9.15.4", "packageManager": "pnpm@9.15.5",
"engines": { "engines": {
"node": ">=22.13.1" "node": ">=22.13.1"
}, },
+1 -1
View File
@@ -49,7 +49,7 @@
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0", "react-dom": "19.0.0",
"superjson": "2.2.2", "superjson": "2.2.2",
"trpc-to-openapi": "^2.1.2", "trpc-to-openapi": "^2.1.3",
"zod": "^3.24.1" "zod": "^3.24.1"
}, },
"devDependencies": { "devDependencies": {
+1 -14
View File
@@ -5,7 +5,7 @@ import { createTRPCReact } from "@trpc/react-query";
import SuperJSON from "superjson"; import SuperJSON from "superjson";
import type { AppRouter } from "."; import type { AppRouter } from ".";
import { createHeadersCallbackForSource } from "./shared"; import { createHeadersCallbackForSource, getTrpcUrl } from "./shared";
export const clientApi = createTRPCReact<AppRouter>(); export const clientApi = createTRPCReact<AppRouter>();
export const fetchApi = createTRPCClient<AppRouter>({ export const fetchApi = createTRPCClient<AppRouter>({
@@ -17,16 +17,3 @@ export const fetchApi = createTRPCClient<AppRouter>({
}), }),
], ],
}); });
function getBaseUrl() {
if (typeof window !== "undefined") return window.location.origin;
return `http://localhost:${process.env.PORT ?? 3000}`;
}
/**
* Creates the full url for the trpc api endpoint
* @returns
*/
export function getTrpcUrl() {
return `${getBaseUrl()}/api/trpc`;
}
+9 -1
View File
@@ -1,6 +1,8 @@
import { z } from "zod";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth"; import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import { generateSecureRandomToken } from "@homarr/common/server"; import { generateSecureRandomToken } from "@homarr/common/server";
import { createId, db } from "@homarr/db"; import { createId, db, eq } from "@homarr/db";
import { apiKeys } from "@homarr/db/schema"; import { apiKeys } from "@homarr/db/schema";
import { createTRPCRouter, permissionRequiredProcedure } from "../trpc"; import { createTRPCRouter, permissionRequiredProcedure } from "../trpc";
@@ -39,4 +41,10 @@ export const apiKeysRouter = createTRPCRouter({
apiKey: `${id}.${randomToken}`, apiKey: `${id}.${randomToken}`,
}; };
}), }),
delete: permissionRequiredProcedure
.requiresPermission("admin")
.input(z.object({ apiKeyId: z.string() }))
.mutation(async ({ ctx, input }) => {
await ctx.db.delete(apiKeys).where(eq(apiKeys.id, input.apiKeyId)).limit(1);
}),
}); });
@@ -206,7 +206,10 @@ export const integrationRouter = createTRPCRouter({
); );
} }
if (input.attemptSearchEngineCreation) { if (
input.attemptSearchEngineCreation &&
integrationDefs[input.kind].category.flatMap((category) => category).includes("search")
) {
const icon = getIconUrl(input.kind); const icon = getIconUrl(input.kind);
await ctx.db.insert(searchEngines).values({ await ctx.db.insert(searchEngines).values({
id: createId(), id: createId(),
@@ -195,6 +195,12 @@ export const searchEngineRouter = createTRPCRouter({
.requiresPermission("search-engine-full-all") .requiresPermission("search-engine-full-all")
.input(validation.common.byId) .input(validation.common.byId)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
await ctx.db
.update(users)
.set({
defaultSearchEngineId: null,
})
.where(eq(users.defaultSearchEngineId, input.id));
await ctx.db.delete(searchEngines).where(eq(searchEngines.id, input.id)); await ctx.db.delete(searchEngines).where(eq(searchEngines.id, input.id));
}), }),
}); });
+5 -1
View File
@@ -6,7 +6,7 @@ import { createTRPCRouter, publicProcedure } from "../../trpc";
export const weatherRouter = createTRPCRouter({ export const weatherRouter = createTRPCRouter({
atLocation: publicProcedure.input(validation.widget.weather.atLocationInput).query(async ({ input }) => { atLocation: publicProcedure.input(validation.widget.weather.atLocationInput).query(async ({ input }) => {
const res = await fetchWithTimeout( const res = await fetchWithTimeout(
`https://api.open-meteo.com/v1/forecast?latitude=${input.latitude}&longitude=${input.longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min&current_weather=true&timezone=auto`, `https://api.open-meteo.com/v1/forecast?latitude=${input.latitude}&longitude=${input.longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min,sunrise,sunset,wind_speed_10m_max,wind_gusts_10m_max&current_weather=true&timezone=auto`,
); );
const json: unknown = await res.json(); const json: unknown = await res.json();
const weather = await validation.widget.weather.atLocationOutput.parseAsync(json); const weather = await validation.widget.weather.atLocationOutput.parseAsync(json);
@@ -18,6 +18,10 @@ export const weatherRouter = createTRPCRouter({
weatherCode: weather.daily.weathercode[index] ?? 404, weatherCode: weather.daily.weathercode[index] ?? 404,
maxTemp: weather.daily.temperature_2m_max[index], maxTemp: weather.daily.temperature_2m_max[index],
minTemp: weather.daily.temperature_2m_min[index], minTemp: weather.daily.temperature_2m_min[index],
sunrise: weather.daily.sunrise[index],
sunset: weather.daily.sunset[index],
maxWindSpeed: weather.daily.wind_speed_10m_max[index],
maxWindGusts: weather.daily.wind_gusts_10m_max[index],
}; };
}), }),
}; };
+15
View File
@@ -36,3 +36,18 @@ async function importCookiesAsync() {
.map(({ name, value }) => `${name}=${value}`) .map(({ name, value }) => `${name}=${value}`)
.join(";"); .join(";");
} }
function getBaseUrl() {
if (typeof window !== "undefined") return window.location.origin;
return `http://${process.env.HOSTNAME ?? "localhost"}:3000`;
}
export const trpcPath = "/api/trpc";
/**
* Creates the full url for the trpc api endpoint
* @returns
*/
export function getTrpcUrl() {
return `${getBaseUrl()}${trpcPath}`;
}
+4
View File
@@ -1,3 +1,7 @@
export const capitalize = <T extends string>(str: T) => { export const capitalize = <T extends string>(str: T) => {
return (str.charAt(0).toUpperCase() + str.slice(1)) as Capitalize<T>; return (str.charAt(0).toUpperCase() + str.slice(1)) as Capitalize<T>;
}; };
export const isNullOrWhitespace = (value: string | null): value is null => {
return value == null || value.trim() === "";
};
+1 -1
View File
@@ -38,7 +38,7 @@
"@homarr/server-settings": "workspace:^0.1.0", "@homarr/server-settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0",
"semver-parser": "^4.1.7" "semver-parser": "^4.1.8"
}, },
"devDependencies": { "devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0", "@homarr/eslint-config": "workspace:^0.2.0",
@@ -0,0 +1,2 @@
-- Custom SQL migration file, put your code below! --
-- This file is empty as there was a bug in the migration script of sqlite, missing on delete actions. See https://github.com/homarr-labs/homarr/pull/2211 --
File diff suppressed because it is too large. Load diff
@@ -162,6 +162,13 @@
"when": 1737927618711, "when": 1737927618711,
"tag": "0022_famous_otto_octavius", "tag": "0022_famous_otto_octavius",
"breakpoints": true "breakpoints": true
},
{
"idx": 23,
"version": "5",
"when": 1738687012272,
"tag": "0023_fix_on_delete_actions",
"breakpoints": true
} }
] ]
} }
@@ -0,0 +1,39 @@
-- Custom SQL migration file, put your code below! --
COMMIT TRANSACTION;
--> statement-breakpoint
PRAGMA foreign_keys = OFF;
--> statement-breakpoint
BEGIN TRANSACTION;
--> statement-breakpoint
CREATE TABLE `__new_user` (
`id` text PRIMARY KEY NOT NULL,
`name` text,
`email` text,
`email_verified` integer,
`image` text,
`password` text,
`salt` text,
`provider` text DEFAULT 'credentials' NOT NULL,
`home_board_id` text,
`mobile_home_board_id` text,
`default_search_engine_id` text,
`open_search_in_new_tab` integer DEFAULT true NOT NULL,
`color_scheme` text DEFAULT 'dark' NOT NULL,
`first_day_of_week` integer DEFAULT 1 NOT NULL,
`ping_icons_enabled` integer DEFAULT false NOT NULL,
FOREIGN KEY (`home_board_id`) REFERENCES `board`(`id`) ON UPDATE no action ON DELETE set null,
FOREIGN KEY (`mobile_home_board_id`) REFERENCES `board`(`id`) ON UPDATE no action ON DELETE set null,
FOREIGN KEY (`default_search_engine_id`) REFERENCES `search_engine`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
INSERT INTO `__new_user`("id", "name", "email", "email_verified", "image", "password", "salt", "provider", "home_board_id", "mobile_home_board_id", "default_search_engine_id", "open_search_in_new_tab", "color_scheme", "first_day_of_week", "ping_icons_enabled") SELECT "id", "name", "email", "email_verified", "image", "password", "salt", "provider", "home_board_id", "mobile_home_board_id", "default_search_engine_id", "open_search_in_new_tab", "color_scheme", "first_day_of_week", "ping_icons_enabled" FROM `user`;
--> statement-breakpoint
DROP TABLE `user`;
--> statement-breakpoint
ALTER TABLE `__new_user` RENAME TO `user`;
--> statement-breakpoint
COMMIT TRANSACTION;
--> statement-breakpoint
PRAGMA foreign_keys = ON;
--> statement-breakpoint
BEGIN TRANSACTION;
File diff suppressed because it is too large. Load diff
@@ -162,6 +162,13 @@
"when": 1737927609085, "when": 1737927609085,
"tag": "0022_modern_sunfire", "tag": "0022_modern_sunfire",
"breakpoints": true "breakpoints": true
},
{
"idx": 23,
"version": "6",
"when": 1738686324915,
"tag": "0023_fix_on_delete_actions",
"breakpoints": true
} }
] ]
} }
+3 -3
View File
@@ -44,11 +44,11 @@
"@homarr/server-settings": "workspace:^0.1.0", "@homarr/server-settings": "workspace:^0.1.0",
"@paralleldrive/cuid2": "^2.2.2", "@paralleldrive/cuid2": "^2.2.2",
"@t3-oss/env-nextjs": "^0.12.0", "@t3-oss/env-nextjs": "^0.12.0",
"@testcontainers/mysql": "^10.17.2", "@testcontainers/mysql": "^10.18.0",
"better-sqlite3": "^11.8.1", "better-sqlite3": "^11.8.1",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"drizzle-kit": "^0.30.3", "drizzle-kit": "^0.30.4",
"drizzle-orm": "^0.39.0", "drizzle-orm": "^0.39.2",
"drizzle-zod": "^0.7.0", "drizzle-zod": "^0.7.0",
"mysql2": "3.12.0" "mysql2": "3.12.0"
}, },
@@ -12,7 +12,7 @@ export interface HealthMonitoring {
}; };
rebootRequired: boolean; rebootRequired: boolean;
availablePkgUpdates: number; availablePkgUpdates: number;
cpuTemp: number; cpuTemp: number | undefined;
fileSystem: { fileSystem: {
deviceName: string; deviceName: string;
used: string; used: string;
@@ -63,9 +63,6 @@ export class OpenMediaVaultIntegration extends Integration {
if (!smartResult.success) { if (!smartResult.success) {
throw new Error("Invalid SMART information response"); throw new Error("Invalid SMART information response");
} }
if (!cpuTempResult.success) {
throw new Error("Invalid CPU temperature response");
}
const fileSystem = fileSystemResult.data.response.map((fileSystem) => ({ const fileSystem = fileSystemResult.data.response.map((fileSystem) => ({
deviceName: fileSystem.devicename, deviceName: fileSystem.devicename,
@@ -94,7 +91,7 @@ export class OpenMediaVaultIntegration extends Integration {
}, },
rebootRequired: systemResult.data.response.rebootRequired, rebootRequired: systemResult.data.response.rebootRequired,
availablePkgUpdates: systemResult.data.response.availablePkgUpdates, availablePkgUpdates: systemResult.data.response.availablePkgUpdates,
cpuTemp: cpuTempResult.data.response.cputemp, cpuTemp: cpuTempResult.success ? cpuTempResult.data.response.cputemp : undefined,
fileSystem, fileSystem,
smart, smart,
}; };
+1 -1
View File
@@ -26,7 +26,7 @@
}, },
"prettier": "@homarr/prettier-config", "prettier": "@homarr/prettier-config",
"dependencies": { "dependencies": {
"ioredis": "5.4.2", "ioredis": "5.5.0",
"superjson": "2.2.2", "superjson": "2.2.2",
"winston": "3.17.0" "winston": "3.17.0"
}, },
+1 -1
View File
@@ -33,7 +33,7 @@
"@homarr/ui": "workspace:^0.1.0", "@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^7.16.2", "@mantine/core": "^7.16.2",
"@tabler/icons-react": "^3.29.0", "@tabler/icons-react": "^3.30.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"next": "15.1.6", "next": "15.1.6",
"react": "19.0.0", "react": "19.0.0",
+1 -1
View File
@@ -25,7 +25,7 @@
"dependencies": { "dependencies": {
"@homarr/ui": "workspace:^0.1.0", "@homarr/ui": "workspace:^0.1.0",
"@mantine/notifications": "^7.16.2", "@mantine/notifications": "^7.16.2",
"@tabler/icons-react": "^3.29.0" "@tabler/icons-react": "^3.30.0"
}, },
"devDependencies": { "devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0", "@homarr/eslint-config": "workspace:^0.2.0",
@@ -56,6 +56,8 @@ const optionMapping: OptionMapping = {
showSeconds: () => undefined, showSeconds: () => undefined,
timezone: (oldOptions) => oldOptions.timezone, timezone: (oldOptions) => oldOptions.timezone,
useCustomTimezone: () => true, useCustomTimezone: () => true,
customTimeFormat: () => undefined,
customDateFormat: () => undefined,
}, },
downloads: { downloads: {
activeTorrentThreshold: (oldOptions) => activeTorrentThreshold: (oldOptions) =>
@@ -77,6 +79,8 @@ const optionMapping: OptionMapping = {
forecastDayCount: (oldOptions) => oldOptions.forecastDays, forecastDayCount: (oldOptions) => oldOptions.forecastDays,
hasForecast: (oldOptions) => oldOptions.displayWeekly, hasForecast: (oldOptions) => oldOptions.displayWeekly,
isFormatFahrenheit: (oldOptions) => oldOptions.displayInFahrenheit, isFormatFahrenheit: (oldOptions) => oldOptions.displayInFahrenheit,
disableTemperatureDecimals: () => undefined,
showCurrentWindSpeed: () => undefined,
location: (oldOptions) => oldOptions.location, location: (oldOptions) => oldOptions.location,
showCity: (oldOptions) => oldOptions.displayCityName, showCity: (oldOptions) => oldOptions.displayCityName,
dateFormat: (oldOptions) => (oldOptions.dateFormat === "hide" ? undefined : oldOptions.dateFormat), dateFormat: (oldOptions) => (oldOptions.dateFormat === "hide" ? undefined : oldOptions.dateFormat),
+3 -1
View File
@@ -22,8 +22,10 @@
}, },
"prettier": "@homarr/prettier-config", "prettier": "@homarr/prettier-config",
"dependencies": { "dependencies": {
"@homarr/certificates": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0", "@homarr/common": "workspace:^0.1.0",
"@homarr/log": "workspace:^0.1.0" "@homarr/log": "workspace:^0.1.0",
"pretty-print-error": "^1.1.2"
}, },
"devDependencies": { "devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0", "@homarr/eslint-config": "workspace:^0.2.0",
+25 -4
View File
@@ -1,13 +1,34 @@
import { extractErrorMessage, fetchWithTimeout } from "@homarr/common"; import { formatError } from "pretty-print-error";
import type { fetch } from "undici";
import { fetchWithTrustedCertificatesAsync } from "@homarr/certificates/server";
import { logger } from "@homarr/log"; import { logger } from "@homarr/log";
export const sendPingRequestAsync = async (url: string) => { export const sendPingRequestAsync = async (url: string) => {
try { try {
return await fetchWithTimeout(url).then((response) => ({ statusCode: response.status })); return await fetchWithTimeoutAndCertificates(url).then((response) => ({ statusCode: response.status }));
} catch (error) { } catch (error) {
logger.error("packages/ping/src/index.ts:", error); logger.error("packages/ping/src/index.ts:", formatError(error));
return { return {
error: extractErrorMessage(error), error: formatError(error),
}; };
} }
}; };
/**
* Same as fetch, but with a timeout of 10 seconds.
* Also respects certificates.
* https://stackoverflow.com/questions/46946380/fetch-api-request-timeout
* @param param0 fetch arguments
* @returns fetch response
*/
export const fetchWithTimeoutAndCertificates = (...[url, requestInit]: Parameters<typeof fetch>) => {
const controller = new AbortController();
// 10 seconds timeout:
const timeoutId = setTimeout(() => controller.abort(), 10000);
return fetchWithTrustedCertificatesAsync(url, { signal: controller.signal, ...requestInit }).finally(() => {
clearTimeout(timeoutId);
});
};
+1 -1
View File
@@ -26,7 +26,7 @@
"@homarr/db": "workspace:^", "@homarr/db": "workspace:^",
"@homarr/definitions": "workspace:^", "@homarr/definitions": "workspace:^",
"@homarr/log": "workspace:^", "@homarr/log": "workspace:^",
"ioredis": "5.4.2", "ioredis": "5.5.0",
"superjson": "2.2.2" "superjson": "2.2.2"
}, },
"devDependencies": { "devDependencies": {
+1 -1
View File
@@ -30,7 +30,7 @@
"@homarr/log": "workspace:^0.1.0", "@homarr/log": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0", "@homarr/redis": "workspace:^0.1.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"octokit": "^4.1.0", "octokit": "^4.1.1",
"pretty-print-error": "^1.1.2", "pretty-print-error": "^1.1.2",
"superjson": "2.2.2" "superjson": "2.2.2"
}, },
+2 -2
View File
@@ -36,8 +36,8 @@
"@mantine/core": "^7.16.2", "@mantine/core": "^7.16.2",
"@mantine/hooks": "^7.16.2", "@mantine/hooks": "^7.16.2",
"@mantine/spotlight": "^7.16.2", "@mantine/spotlight": "^7.16.2",
"@tabler/icons-react": "^3.29.0", "@tabler/icons-react": "^3.30.0",
"jotai": "^2.11.1", "jotai": "^2.11.3",
"next": "15.1.6", "next": "15.1.6",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0", "react-dom": "19.0.0",
File diff suppressed because it is too large. Load diff
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "应用", "apply": "应用",
"backToOverview": "返回概览", "backToOverview": "返回概览",
"create": "创建", "create": "创建",
"createAnother": "",
"edit": "编辑", "edit": "编辑",
"import": "导入", "import": "导入",
"insert": "插入", "insert": "插入",
@@ -946,7 +947,8 @@
"moveUp": "上移", "moveUp": "上移",
"moveDown": "下移", "moveDown": "下移",
"createAbove": "上方新建分类", "createAbove": "上方新建分类",
"createBelow": "下方新建分类" "createBelow": "下方新建分类",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "新建分类", "title": "新建分类",
@@ -965,6 +967,10 @@
"create": "新建分类", "create": "新建分类",
"changePosition": "换位" "changePosition": "换位"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "日期格式", "label": "日期格式",
"description": "日期应该是什么样的" "description": "日期应该是什么样的"
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "华氏温度" "label": "华氏温度"
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "天气位置" "label": "天气位置"
}, },
@@ -1391,6 +1412,13 @@
"description": "日期应该是什么样的" "description": "日期应该是什么样的"
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "晴朗", "clear": "晴朗",
"mainlyClear": "晴朗为主", "mainlyClear": "晴朗为主",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "创建 API 令牌" "createApiToken": "创建 API 令牌"
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "ID", "id": "ID",
"createdBy": "创建者" "createdBy": "创建者",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Použít", "apply": "Použít",
"backToOverview": "Zpět na přehled", "backToOverview": "Zpět na přehled",
"create": "Vytvořit", "create": "Vytvořit",
"createAnother": "",
"edit": "Upravit", "edit": "Upravit",
"import": "Importovat", "import": "Importovat",
"insert": "Vložit", "insert": "Vložit",
@@ -946,7 +947,8 @@
"moveUp": "Posunout nahoru", "moveUp": "Posunout nahoru",
"moveDown": "Posunout dolů", "moveDown": "Posunout dolů",
"createAbove": "Nová kategorie nad", "createAbove": "Nová kategorie nad",
"createBelow": "Nová kategorie pod" "createBelow": "Nová kategorie pod",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "Nová kategorie", "title": "Nová kategorie",
@@ -965,6 +967,10 @@
"create": "Nová kategorie", "create": "Nová kategorie",
"changePosition": "Změnit pozici" "changePosition": "Změnit pozici"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Formát data", "label": "Formát data",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Teplota ve Fahrenheitech" "label": "Teplota ve Fahrenheitech"
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Lokalita pro počasí" "label": "Lokalita pro počasí"
}, },
@@ -1391,6 +1412,13 @@
"description": "Jak by datum mělo vypadat" "description": "Jak by datum mělo vypadat"
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Jasno", "clear": "Jasno",
"mainlyClear": "Převážně jasno", "mainlyClear": "Převážně jasno",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+43 -8
View File
@@ -28,7 +28,7 @@
}, },
"boardSelection": { "boardSelection": {
"title": "Fundet {count} tavler", "title": "Fundet {count} tavler",
"description": "Vælg alle tavler med deres størrelser du vil importere", "description": "Vælg alle tavler med den størrelse, du vil importere",
"action": { "action": {
"selectAll": "Vælg alle", "selectAll": "Vælg alle",
"unselectAll": "Fravælg alle" "unselectAll": "Fravælg alle"
@@ -153,10 +153,10 @@
"label": "Brug ikoner til pings" "label": "Brug ikoner til pings"
}, },
"defaultSearchEngine": { "defaultSearchEngine": {
"label": "" "label": "Standardsøgemaskine"
}, },
"openSearchInNewTab": { "openSearchInNewTab": {
"label": "" "label": "Åben søgeresultater i en ny fane"
} }
}, },
"error": { "error": {
@@ -219,10 +219,10 @@
"changeSearchPreferences": { "changeSearchPreferences": {
"notification": { "notification": {
"success": { "success": {
"message": "" "message": "Søgeindstillingerne blev ændret"
}, },
"error": { "error": {
"message": "" "message": "Kan ikke ændre søgeindstillinger"
} }
} }
}, },
@@ -805,6 +805,7 @@
"apply": "Anvend", "apply": "Anvend",
"backToOverview": "Tilbage til oversigt", "backToOverview": "Tilbage til oversigt",
"create": "Opret", "create": "Opret",
"createAnother": "Opret og start forfra",
"edit": "Rediger", "edit": "Rediger",
"import": "Importér", "import": "Importér",
"insert": "Indsæt", "insert": "Indsæt",
@@ -946,7 +947,8 @@
"moveUp": "Flyt op", "moveUp": "Flyt op",
"moveDown": "Flyt ned", "moveDown": "Flyt ned",
"createAbove": "Ny kategori ovenover", "createAbove": "Ny kategori ovenover",
"createBelow": "Ny kategori nedenunder" "createBelow": "Ny kategori nedenunder",
"openAllInNewTabs": "Åbn alt i faneblade"
}, },
"create": { "create": {
"title": "Ny kategori", "title": "Ny kategori",
@@ -965,6 +967,10 @@
"create": "Ny kategori", "create": "Ny kategori",
"changePosition": "Ændre placering" "changePosition": "Ændre placering"
} }
},
"openAllInNewTabs": {
"title": "Åbn alt i faneblade",
"text": "Nogle browsere kan blokere bulk-åbning af faner af sikkerhedsmæssige årsager. Homarr kunne ikke åbne alle vinduer, fordi din browser blokerede denne handling. Tillad venligst \"Åbn pop op-vinduer\" og prøv igen."
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Datoformat", "label": "Datoformat",
"description": "Hvordan datoen skal se ud" "description": "Hvordan datoen skal se ud"
},
"customTimeFormat": {
"label": "Brugerdefineret tidsformat",
"description": "Brug ISO 8601 til at formatere tid (dette vil tilsidesætte andre valgmuligheder)"
},
"customDateFormat": {
"label": "Brugerdefineret datoformat",
"description": "Brug ISO 8601 til at formatere dato (dette vil tilsidesætte andre valg)"
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Temperatur i Fahrenheit" "label": "Temperatur i Fahrenheit"
}, },
"disableTemperatureDecimals": {
"label": "Deaktiver temperaturdecimaler"
},
"showCurrentWindSpeed": {
"label": "Vis aktuel vindhastighed",
"description": "Kun på nuværende vejr"
},
"location": { "location": {
"label": "Vejr lokation" "label": "Vejr lokation"
}, },
@@ -1391,6 +1412,13 @@
"description": "Hvordan datoen skal se ud" "description": "Hvordan datoen skal se ud"
} }
}, },
"currentWindSpeed": "{currentWindSpeed} km/t",
"dailyForecast": {
"sunrise": "Solopgang",
"sunset": "Solnedgang",
"maxWindSpeed": "Maks. vindhastighed: {maxWindSpeed} km/t",
"maxWindGusts": "Maks. vindstød {maxWindGusts} km/t"
},
"kind": { "kind": {
"clear": "Skyfrit", "clear": "Skyfrit",
"mainlyClear": "Hovedsageligt skyfrit", "mainlyClear": "Hovedsageligt skyfrit",
@@ -2287,7 +2315,7 @@
"mobile": "Mobil" "mobile": "Mobil"
} }
}, },
"search": "", "search": "Søg",
"firstDayOfWeek": "Første ugedag", "firstDayOfWeek": "Første ugedag",
"accessibility": "Hjælpefunktioner" "accessibility": "Hjælpefunktioner"
} }
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "Opret API token" "createApiToken": "Opret API token"
}, },
"modal": {
"delete": {
"title": "Slet API token",
"text": "Dette vil permanent slette API-token. API-klienter der bruger dette token kan ikke længere godkende og udføre API-anmodninger. Denne handling kan ikke fortrydes."
}
},
"table": { "table": {
"header": { "header": {
"id": "ID", "id": "ID",
"createdBy": "Oprettet af" "createdBy": "Oprettet af",
"actions": "Handlinger"
} }
} }
} }
File diff suppressed because it is too large. Load diff
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Εφαρμογή", "apply": "Εφαρμογή",
"backToOverview": "", "backToOverview": "",
"create": "Δημιουργία", "create": "Δημιουργία",
"createAnother": "",
"edit": "Επεξεργασία", "edit": "Επεξεργασία",
"import": "", "import": "",
"insert": "Εισαγωγή", "insert": "Εισαγωγή",
@@ -946,7 +947,8 @@
"moveUp": "Μετακίνηση επάνω", "moveUp": "Μετακίνηση επάνω",
"moveDown": "Μετακίνηση κάτω", "moveDown": "Μετακίνηση κάτω",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Αλλαγή θέσης" "changePosition": "Αλλαγή θέσης"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Τοποθεσία καιρού" "label": "Τοποθεσία καιρού"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Καθαρός", "clear": "Καθαρός",
"mainlyClear": "Κυρίως καθαρός", "mainlyClear": "Κυρίως καθαρός",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "Αναγνωριστικό (ID)", "id": "Αναγνωριστικό (ID)",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Apply", "apply": "Apply",
"backToOverview": "Back to overview", "backToOverview": "Back to overview",
"create": "Create", "create": "Create",
"createAnother": "Create and start over",
"edit": "Edit", "edit": "Edit",
"import": "Import", "import": "Import",
"insert": "Insert", "insert": "Insert",
@@ -946,7 +947,8 @@
"moveUp": "Move up", "moveUp": "Move up",
"moveDown": "Move down", "moveDown": "Move down",
"createAbove": "New category above", "createAbove": "New category above",
"createBelow": "New category below" "createBelow": "New category below",
"openAllInNewTabs": "Open all in tabs"
}, },
"create": { "create": {
"title": "New category", "title": "New category",
@@ -965,6 +967,10 @@
"create": "New category", "create": "New category",
"changePosition": "Change position" "changePosition": "Change position"
} }
},
"openAllInNewTabs": {
"title": "Open all in tabs",
"text": "Some browsers may block the bulk-opening of tabs for security reasons. Homarr was unable to open all windows, because your browser blocked this action. Please allow \"Open pop-up windows\" and re-try."
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Date Format", "label": "Date Format",
"description": "How the date should look like" "description": "How the date should look like"
},
"customTimeFormat": {
"label": "Custom time format",
"description": "Use ISO 8601 to format time (this will override other options)"
},
"customDateFormat": {
"label": "Custom date format",
"description": "Use ISO 8601 to format date (this will override other options)"
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Temperature in Fahrenheit" "label": "Temperature in Fahrenheit"
}, },
"disableTemperatureDecimals": {
"label": "Disable temperature decimals"
},
"showCurrentWindSpeed": {
"label": "Show current wind speed",
"description": "Only on current weather"
},
"location": { "location": {
"label": "Weather location" "label": "Weather location"
}, },
@@ -1391,6 +1412,13 @@
"description": "How the date should look like" "description": "How the date should look like"
} }
}, },
"currentWindSpeed": "{currentWindSpeed} km/h",
"dailyForecast": {
"sunrise": "Sunrise",
"sunset": "Sunset",
"maxWindSpeed": "Max wind speed: {maxWindSpeed} km/h",
"maxWindGusts": "Max wind gusts: {maxWindGusts} km/h"
},
"kind": { "kind": {
"clear": "Clear", "clear": "Clear",
"mainlyClear": "Mainly clear", "mainlyClear": "Mainly clear",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "Create API token" "createApiToken": "Create API token"
}, },
"modal": {
"delete": {
"title": "Delete API token",
"text": "This will permanently delete the API token. API clients using this token can no longer authenticate and perform API requests. This action cannot be undone."
}
},
"table": { "table": {
"header": { "header": {
"id": "ID", "id": "ID",
"createdBy": "Created by" "createdBy": "Created by",
"actions": "Actions"
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Aplicar", "apply": "Aplicar",
"backToOverview": "", "backToOverview": "",
"create": "Crear", "create": "Crear",
"createAnother": "",
"edit": "Editar", "edit": "Editar",
"import": "", "import": "",
"insert": "Insertar", "insert": "Insertar",
@@ -946,7 +947,8 @@
"moveUp": "Mover hacia arriba", "moveUp": "Mover hacia arriba",
"moveDown": "Mover hacia abajo", "moveDown": "Mover hacia abajo",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Cambiar posición" "changePosition": "Cambiar posición"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Ubicación" "label": "Ubicación"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Despejado", "clear": "Despejado",
"mainlyClear": "Mayormente despejado", "mainlyClear": "Mayormente despejado",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "", "apply": "",
"backToOverview": "", "backToOverview": "",
"create": "", "create": "",
"createAnother": "",
"edit": "", "edit": "",
"import": "", "import": "",
"insert": "", "insert": "",
@@ -946,7 +947,8 @@
"moveUp": "", "moveUp": "",
"moveDown": "", "moveDown": "",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "" "changePosition": ""
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "" "label": ""
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "", "clear": "",
"mainlyClear": "", "mainlyClear": "",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Appliquer", "apply": "Appliquer",
"backToOverview": "", "backToOverview": "",
"create": "Créer", "create": "Créer",
"createAnother": "",
"edit": "Modifier", "edit": "Modifier",
"import": "Importer", "import": "Importer",
"insert": "Insérer", "insert": "Insérer",
@@ -946,7 +947,8 @@
"moveUp": "Monter", "moveUp": "Monter",
"moveDown": "Descendre", "moveDown": "Descendre",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Modifier la position" "changePosition": "Modifier la position"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Lieu de la météo" "label": "Lieu de la météo"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Clair", "clear": "Clair",
"mainlyClear": "Principalement clair", "mainlyClear": "Principalement clair",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+43 -8
View File
@@ -28,7 +28,7 @@
}, },
"boardSelection": { "boardSelection": {
"title": "נמצאו {count} לוחות", "title": "נמצאו {count} לוחות",
"description": "בחר את כל הלוחות שברצונך לייבא", "description": "בחר את כל הלוחות עם הגודל שברצונך לייבא",
"action": { "action": {
"selectAll": "בחר הכל", "selectAll": "בחר הכל",
"unselectAll": "בטל את הבחירה בכולם" "unselectAll": "בטל את הבחירה בכולם"
@@ -153,10 +153,10 @@
"label": "השתמש בסמלים עבור פינגים" "label": "השתמש בסמלים עבור פינגים"
}, },
"defaultSearchEngine": { "defaultSearchEngine": {
"label": "" "label": "מנוע חיפוש ברירת מחדל"
}, },
"openSearchInNewTab": { "openSearchInNewTab": {
"label": "" "label": "פתיחת תוצאות חיפוש בכרטיסיה חדשה"
} }
}, },
"error": { "error": {
@@ -219,10 +219,10 @@
"changeSearchPreferences": { "changeSearchPreferences": {
"notification": { "notification": {
"success": { "success": {
"message": "" "message": "העדפות חיפוש השתנו בהצלחה"
}, },
"error": { "error": {
"message": "" "message": "לא ניתן לשנות העדפות חיפוש"
} }
} }
}, },
@@ -805,6 +805,7 @@
"apply": "החל", "apply": "החל",
"backToOverview": "חזרה לסקירה כללית", "backToOverview": "חזרה לסקירה כללית",
"create": "צור", "create": "צור",
"createAnother": "",
"edit": "עריכה", "edit": "עריכה",
"import": "ייבוא", "import": "ייבוא",
"insert": "הוספה", "insert": "הוספה",
@@ -946,7 +947,8 @@
"moveUp": "הזזה למעלה", "moveUp": "הזזה למעלה",
"moveDown": "הזזה למטה", "moveDown": "הזזה למטה",
"createAbove": "קטגוריה חדשה למעלה", "createAbove": "קטגוריה חדשה למעלה",
"createBelow": "קטגוריה חדשה למטה" "createBelow": "קטגוריה חדשה למטה",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "קטגוריה חדשה", "title": "קטגוריה חדשה",
@@ -965,6 +967,10 @@
"create": "קטגוריה חדשה", "create": "קטגוריה חדשה",
"changePosition": "שנה מיקום" "changePosition": "שנה מיקום"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "פורמט תאריך", "label": "פורמט תאריך",
"description": "איך צריך להיראות התאריך" "description": "איך צריך להיראות התאריך"
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "טמפרטורה בפרנהייט" "label": "טמפרטורה בפרנהייט"
}, },
"disableTemperatureDecimals": {
"label": "ביטול טמפרטורה עשרונית"
},
"showCurrentWindSpeed": {
"label": "הצגת מהירות רוח נוכחית",
"description": "רק במזג אוויר נוכחי"
},
"location": { "location": {
"label": "מיקום מזג האוויר" "label": "מיקום מזג האוויר"
}, },
@@ -1391,6 +1412,13 @@
"description": "איך צריך להיראות התאריך" "description": "איך צריך להיראות התאריך"
} }
}, },
"currentWindSpeed": "{currentWindSpeed} קמ״ש",
"dailyForecast": {
"sunrise": "זריחה",
"sunset": "שקיעה",
"maxWindSpeed": "מהירות רוח מקסימלית: {maxWindSpeed} קמ״ש",
"maxWindGusts": "משבי רוח מקסימלים: {maxWindGusts} קמ״ש"
},
"kind": { "kind": {
"clear": "בהיר", "clear": "בהיר",
"mainlyClear": "בהיר בעיקר", "mainlyClear": "בהיר בעיקר",
@@ -2287,7 +2315,7 @@
"mobile": "מכשיר נייד" "mobile": "מכשיר נייד"
} }
}, },
"search": "", "search": "חיפוש",
"firstDayOfWeek": "היום הראשון בשבוע", "firstDayOfWeek": "היום הראשון בשבוע",
"accessibility": "נגישות" "accessibility": "נגישות"
} }
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "יצירת מפתח  API" "createApiToken": "יצירת מפתח  API"
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "מספר מזהה", "id": "מספר מזהה",
"createdBy": "נוצר על ידי" "createdBy": "נוצר על ידי",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "", "apply": "",
"backToOverview": "", "backToOverview": "",
"create": "Stvoriti", "create": "Stvoriti",
"createAnother": "",
"edit": "Uredi", "edit": "Uredi",
"import": "", "import": "",
"insert": "", "insert": "",
@@ -946,7 +947,8 @@
"moveUp": "Pomakni se gore", "moveUp": "Pomakni se gore",
"moveDown": "Pomicati prema dolje", "moveDown": "Pomicati prema dolje",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Promijenjen položaj" "changePosition": "Promijenjen položaj"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Lokacija vremenske prognoze" "label": "Lokacija vremenske prognoze"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Vedro", "clear": "Vedro",
"mainlyClear": "Uglavnom vedro", "mainlyClear": "Uglavnom vedro",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "iskaznica", "id": "iskaznica",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Alkalmaz", "apply": "Alkalmaz",
"backToOverview": "Vissza az áttekintéshez", "backToOverview": "Vissza az áttekintéshez",
"create": "Létrehozás", "create": "Létrehozás",
"createAnother": "",
"edit": "Szerkesztés", "edit": "Szerkesztés",
"import": "Importálás", "import": "Importálás",
"insert": "Beillesztés", "insert": "Beillesztés",
@@ -946,7 +947,8 @@
"moveUp": "Felfelé mozgatás", "moveUp": "Felfelé mozgatás",
"moveDown": "Mozgatás le", "moveDown": "Mozgatás le",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Pozíció módosítása" "changePosition": "Pozíció módosítása"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Dátum formátum", "label": "Dátum formátum",
"description": "Hogyan nézzen ki a dátum" "description": "Hogyan nézzen ki a dátum"
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Időjárás helye" "label": "Időjárás helye"
}, },
@@ -1391,6 +1412,13 @@
"description": "Hogyan nézzen ki a dátum" "description": "Hogyan nézzen ki a dátum"
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Tiszta", "clear": "Tiszta",
"mainlyClear": "Főként tiszta", "mainlyClear": "Főként tiszta",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "Azonosító", "id": "Azonosító",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Applica", "apply": "Applica",
"backToOverview": "", "backToOverview": "",
"create": "Crea", "create": "Crea",
"createAnother": "",
"edit": "Modifica", "edit": "Modifica",
"import": "", "import": "",
"insert": "Inserisci", "insert": "Inserisci",
@@ -946,7 +947,8 @@
"moveUp": "Sposta in alto", "moveUp": "Sposta in alto",
"moveDown": "Sposta in basso", "moveDown": "Sposta in basso",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Cambia posizione" "changePosition": "Cambia posizione"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Località meteo" "label": "Località meteo"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Sereno", "clear": "Sereno",
"mainlyClear": "Per lo più sereno", "mainlyClear": "Per lo più sereno",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "適用", "apply": "適用",
"backToOverview": "", "backToOverview": "",
"create": "作成", "create": "作成",
"createAnother": "",
"edit": "編集", "edit": "編集",
"import": "", "import": "",
"insert": "挿入", "insert": "挿入",
@@ -946,7 +947,8 @@
"moveUp": "上に移動", "moveUp": "上に移動",
"moveDown": "下へ移動", "moveDown": "下へ移動",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "ポジションを変更する" "changePosition": "ポジションを変更する"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "天候の場所" "label": "天候の場所"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "クリア", "clear": "クリア",
"mainlyClear": "主なクリア事項", "mainlyClear": "主なクリア事項",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "", "apply": "",
"backToOverview": "", "backToOverview": "",
"create": "만들기", "create": "만들기",
"createAnother": "",
"edit": "수정", "edit": "수정",
"import": "", "import": "",
"insert": "", "insert": "",
@@ -946,7 +947,8 @@
"moveUp": "위로 이동", "moveUp": "위로 이동",
"moveDown": "아래로 이동", "moveDown": "아래로 이동",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "위치 변경" "changePosition": "위치 변경"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "날씨 위치" "label": "날씨 위치"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "맑음", "clear": "맑음",
"mainlyClear": "대체로 맑음", "mainlyClear": "대체로 맑음",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "", "apply": "",
"backToOverview": "", "backToOverview": "",
"create": "Sukurti", "create": "Sukurti",
"createAnother": "",
"edit": "", "edit": "",
"import": "", "import": "",
"insert": "", "insert": "",
@@ -946,7 +947,8 @@
"moveUp": "Pakelti aukštyn", "moveUp": "Pakelti aukštyn",
"moveDown": "Perkelti žemyn", "moveDown": "Perkelti žemyn",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "" "changePosition": ""
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "" "label": ""
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "", "clear": "",
"mainlyClear": "", "mainlyClear": "",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Lietot", "apply": "Lietot",
"backToOverview": "", "backToOverview": "",
"create": "Izveidot", "create": "Izveidot",
"createAnother": "",
"edit": "Rediģēt", "edit": "Rediģēt",
"import": "", "import": "",
"insert": "Ievietot", "insert": "Ievietot",
@@ -946,7 +947,8 @@
"moveUp": "Virzīt augšup", "moveUp": "Virzīt augšup",
"moveDown": "Virzīt lejup", "moveDown": "Virzīt lejup",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Mainīt pozīciju" "changePosition": "Mainīt pozīciju"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Laikapstākļu atrašānās vieta" "label": "Laikapstākļu atrašānās vieta"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Skaidrs", "clear": "Skaidrs",
"mainlyClear": "Galvenokārt skaidrs", "mainlyClear": "Galvenokārt skaidrs",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Toepassen", "apply": "Toepassen",
"backToOverview": "Terug naar overzicht", "backToOverview": "Terug naar overzicht",
"create": "Aanmaken", "create": "Aanmaken",
"createAnother": "",
"edit": "Bewerken", "edit": "Bewerken",
"import": "Importeren", "import": "Importeren",
"insert": "Invoegen", "insert": "Invoegen",
@@ -946,7 +947,8 @@
"moveUp": "Omhoog", "moveUp": "Omhoog",
"moveDown": "Omlaag", "moveDown": "Omlaag",
"createAbove": "Nieuwe categorie hierboven", "createAbove": "Nieuwe categorie hierboven",
"createBelow": "Nieuwe categorie hieronder" "createBelow": "Nieuwe categorie hieronder",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "Nieuwe categorie", "title": "Nieuwe categorie",
@@ -965,6 +967,10 @@
"create": "Nieuwe categorie", "create": "Nieuwe categorie",
"changePosition": "Positie wijzigen" "changePosition": "Positie wijzigen"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Datumnotatie", "label": "Datumnotatie",
"description": "Hoe de datum eruit moet zien" "description": "Hoe de datum eruit moet zien"
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Temperatuur in fahrenheit" "label": "Temperatuur in fahrenheit"
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Weerlocatie" "label": "Weerlocatie"
}, },
@@ -1391,6 +1412,13 @@
"description": "Hoe de datum eruit moet zien" "description": "Hoe de datum eruit moet zien"
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Wissen", "clear": "Wissen",
"mainlyClear": "Overwegend helder", "mainlyClear": "Overwegend helder",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "API-token aanmaken" "createApiToken": "API-token aanmaken"
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "ID", "id": "ID",
"createdBy": "Aangemaakt door" "createdBy": "Aangemaakt door",
"actions": ""
} }
} }
} }
File diff suppressed because it is too large. Load diff
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Zastosuj", "apply": "Zastosuj",
"backToOverview": "Powrót do widoku ogólnego", "backToOverview": "Powrót do widoku ogólnego",
"create": "Utwórz", "create": "Utwórz",
"createAnother": "",
"edit": "Edytuj", "edit": "Edytuj",
"import": "Importuj", "import": "Importuj",
"insert": "Wstaw", "insert": "Wstaw",
@@ -946,7 +947,8 @@
"moveUp": "Przenieś w górę", "moveUp": "Przenieś w górę",
"moveDown": "Przenieś w dół", "moveDown": "Przenieś w dół",
"createAbove": "Nowa kategoria powyżej", "createAbove": "Nowa kategoria powyżej",
"createBelow": "Nowa kategoria poniżej" "createBelow": "Nowa kategoria poniżej",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "Nowa kategoria", "title": "Nowa kategoria",
@@ -965,6 +967,10 @@
"create": "Nowa kategoria", "create": "Nowa kategoria",
"changePosition": "Zmiana pozycji" "changePosition": "Zmiana pozycji"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Format daty", "label": "Format daty",
"description": "Jak powinna wyglądać data" "description": "Jak powinna wyglądać data"
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Temperatura w Fahrenheit" "label": "Temperatura w Fahrenheit"
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Lokalizacja pogody" "label": "Lokalizacja pogody"
}, },
@@ -1391,6 +1412,13 @@
"description": "Jak powinna wyglądać data" "description": "Jak powinna wyglądać data"
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Bezchmurnie", "clear": "Bezchmurnie",
"mainlyClear": "Częściowe zachmurzenie", "mainlyClear": "Częściowe zachmurzenie",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Aplicar", "apply": "Aplicar",
"backToOverview": "", "backToOverview": "",
"create": "Criar", "create": "Criar",
"createAnother": "",
"edit": "Editar", "edit": "Editar",
"import": "", "import": "",
"insert": "Inserir", "insert": "Inserir",
@@ -946,7 +947,8 @@
"moveUp": "Subir", "moveUp": "Subir",
"moveDown": "Mover para baixo", "moveDown": "Mover para baixo",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Mudar de posição" "changePosition": "Mudar de posição"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Localização do tempo" "label": "Localização do tempo"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Limpar", "clear": "Limpar",
"mainlyClear": "Principalmente claro", "mainlyClear": "Principalmente claro",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Aplică", "apply": "Aplică",
"backToOverview": "", "backToOverview": "",
"create": "Creează", "create": "Creează",
"createAnother": "",
"edit": "Editare", "edit": "Editare",
"import": "", "import": "",
"insert": "Introdu", "insert": "Introdu",
@@ -946,7 +947,8 @@
"moveUp": "Mută în sus", "moveUp": "Mută în sus",
"moveDown": "Mută în jos", "moveDown": "Mută în jos",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Schimbă locația" "changePosition": "Schimbă locația"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Locație meteo" "label": "Locație meteo"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Senin", "clear": "Senin",
"mainlyClear": "Parțial noros", "mainlyClear": "Parțial noros",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+42 -7
View File
@@ -153,10 +153,10 @@
"label": "Использовать индикаторы доступности" "label": "Использовать индикаторы доступности"
}, },
"defaultSearchEngine": { "defaultSearchEngine": {
"label": "" "label": "Поисковая система по умолчанию"
}, },
"openSearchInNewTab": { "openSearchInNewTab": {
"label": "" "label": "Открыть результаты поиска в новой вкладке"
} }
}, },
"error": { "error": {
@@ -219,10 +219,10 @@
"changeSearchPreferences": { "changeSearchPreferences": {
"notification": { "notification": {
"success": { "success": {
"message": "" "message": "Настройки поиска успешно изменены"
}, },
"error": { "error": {
"message": "" "message": "Не удается изменить настройки поиска"
} }
} }
}, },
@@ -805,6 +805,7 @@
"apply": "Применить", "apply": "Применить",
"backToOverview": "Вернуться к обзору", "backToOverview": "Вернуться к обзору",
"create": "Создать", "create": "Создать",
"createAnother": "Создать и начать заново",
"edit": "Редактировать", "edit": "Редактировать",
"import": "Импортировать", "import": "Импортировать",
"insert": "Вставить", "insert": "Вставить",
@@ -946,7 +947,8 @@
"moveUp": "Переместить вверх", "moveUp": "Переместить вверх",
"moveDown": "Переместить вниз", "moveDown": "Переместить вниз",
"createAbove": "Создать категорию выше", "createAbove": "Создать категорию выше",
"createBelow": "Создать категорию ниже" "createBelow": "Создать категорию ниже",
"openAllInNewTabs": "Открыть все во вкладках"
}, },
"create": { "create": {
"title": "Новая категория", "title": "Новая категория",
@@ -965,6 +967,10 @@
"create": "Новая категория", "create": "Новая категория",
"changePosition": "Изменить позицию" "changePosition": "Изменить позицию"
} }
},
"openAllInNewTabs": {
"title": "Открыть все во вкладках",
"text": "Некоторые браузеры могут блокировать широкое открытие вкладок по соображениям безопасности. Homarr не смог открыть все окна, потому что ваш браузер заблокировал это действие. Пожалуйста, разрешите \"Открыть всплывающие окна\" и повторите попытку."
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Формат даты", "label": "Формат даты",
"description": "Как должна выглядеть дата" "description": "Как должна выглядеть дата"
},
"customTimeFormat": {
"label": "Пользовательский формат времени",
"description": "Использовать ISO 8601 для форматирования времени (это переопределит другие параметры)"
},
"customDateFormat": {
"label": "Пользовательский формат даты",
"description": "Использовать ISO 8601 для форматирования даты (это переопределит другие параметры)"
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Температура в градусах Фаренгейта" "label": "Температура в градусах Фаренгейта"
}, },
"disableTemperatureDecimals": {
"label": "Отключить десятичные дроби у температур"
},
"showCurrentWindSpeed": {
"label": "Показать текущую скорость ветра",
"description": "Только при текущей погоде"
},
"location": { "location": {
"label": "Местоположение" "label": "Местоположение"
}, },
@@ -1391,6 +1412,13 @@
"description": "Как должна отображаться дата" "description": "Как должна отображаться дата"
} }
}, },
"currentWindSpeed": "{currentWindSpeed} км/ч",
"dailyForecast": {
"sunrise": "Восход",
"sunset": "Закат",
"maxWindSpeed": "Максимальная скорость ветра: {maxWindSpeed} км/ч",
"maxWindGusts": "Максимальные порывы ветра: {maxWindGusts} км/ч"
},
"kind": { "kind": {
"clear": "Ясно", "clear": "Ясно",
"mainlyClear": "Преимущественно ясно", "mainlyClear": "Преимущественно ясно",
@@ -2287,7 +2315,7 @@
"mobile": "Мобильная" "mobile": "Мобильная"
} }
}, },
"search": "", "search": "Поиск",
"firstDayOfWeek": "Первый день недели", "firstDayOfWeek": "Первый день недели",
"accessibility": "Специальные возможности" "accessibility": "Специальные возможности"
} }
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "Создать API-токен" "createApiToken": "Создать API-токен"
}, },
"modal": {
"delete": {
"title": "Удалить API-токен",
"text": "Это навсегда удалит API токен. API клиенты с помощью этого токена больше не могут аутентифицировать и выполнить API запросы. Это действие нельзя отменить."
}
},
"table": { "table": {
"header": { "header": {
"id": "ID", "id": "ID",
"createdBy": "Создан пользователем" "createdBy": "Создан пользователем",
"actions": "Действия"
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Použiť", "apply": "Použiť",
"backToOverview": "Späť na prehľad", "backToOverview": "Späť na prehľad",
"create": "Vytvoriť", "create": "Vytvoriť",
"createAnother": "",
"edit": "Upraviť", "edit": "Upraviť",
"import": "Importovať", "import": "Importovať",
"insert": "Vložiť", "insert": "Vložiť",
@@ -946,7 +947,8 @@
"moveUp": "Posunúť nahor", "moveUp": "Posunúť nahor",
"moveDown": "Posunúť nadol", "moveDown": "Posunúť nadol",
"createAbove": "Nová kategória vyššie", "createAbove": "Nová kategória vyššie",
"createBelow": "Nová kategória nižšie" "createBelow": "Nová kategória nižšie",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "Nová kategória", "title": "Nová kategória",
@@ -965,6 +967,10 @@
"create": "Nová kategória", "create": "Nová kategória",
"changePosition": "Zmeniť pozíciu" "changePosition": "Zmeniť pozíciu"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Formát Dátumu", "label": "Formát Dátumu",
"description": "Ako by mal dátum vyzerať" "description": "Ako by mal dátum vyzerať"
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Teplota vo stupňoch Fahrenheita" "label": "Teplota vo stupňoch Fahrenheita"
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Poloha počasia" "label": "Poloha počasia"
}, },
@@ -1391,6 +1412,13 @@
"description": "Ako by mal dátum vyzerať" "description": "Ako by mal dátum vyzerať"
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Jasno", "clear": "Jasno",
"mainlyClear": "Prevažne jasno", "mainlyClear": "Prevažne jasno",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "Vytvorte token API" "createApiToken": "Vytvorte token API"
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "ID", "id": "ID",
"createdBy": "Vytvoril/a" "createdBy": "Vytvoril/a",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Uporabi", "apply": "Uporabi",
"backToOverview": "", "backToOverview": "",
"create": "Ustvarite spletno stran", "create": "Ustvarite spletno stran",
"createAnother": "",
"edit": "Uredi", "edit": "Uredi",
"import": "", "import": "",
"insert": "Vstavite", "insert": "Vstavite",
@@ -946,7 +947,8 @@
"moveUp": "Premaknite se navzgor", "moveUp": "Premaknite se navzgor",
"moveDown": "Premaknite se navzdol", "moveDown": "Premaknite se navzdol",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Spremeni položaj" "changePosition": "Spremeni položaj"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Lokacija vremena" "label": "Lokacija vremena"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Počisti", "clear": "Počisti",
"mainlyClear": "Večinoma jasno", "mainlyClear": "Večinoma jasno",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Verkställ", "apply": "Verkställ",
"backToOverview": "", "backToOverview": "",
"create": "Skapa", "create": "Skapa",
"createAnother": "",
"edit": "Redigera", "edit": "Redigera",
"import": "", "import": "",
"insert": "Infoga", "insert": "Infoga",
@@ -946,7 +947,8 @@
"moveUp": "Flytta uppåt", "moveUp": "Flytta uppåt",
"moveDown": "Flytta nedåt", "moveDown": "Flytta nedåt",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Ändra position" "changePosition": "Ändra position"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Plats för väder" "label": "Plats för väder"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Klart", "clear": "Klart",
"mainlyClear": "Främst klart", "mainlyClear": "Främst klart",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "", "id": "",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+66 -31
View File
@@ -153,10 +153,10 @@
"label": "Pingler İçin Simgeler Kullanın" "label": "Pingler İçin Simgeler Kullanın"
}, },
"defaultSearchEngine": { "defaultSearchEngine": {
"label": "" "label": "Öntanımlı arama motoru"
}, },
"openSearchInNewTab": { "openSearchInNewTab": {
"label": "" "label": "Arama sonucunu yeni sekmede aç"
} }
}, },
"error": { "error": {
@@ -219,10 +219,10 @@
"changeSearchPreferences": { "changeSearchPreferences": {
"notification": { "notification": {
"success": { "success": {
"message": "" "message": "Arama tercihleri ​​başarıyla değiştirildi"
}, },
"error": { "error": {
"message": "" "message": "Arama tercihleri ​​değiştirilemiyor"
} }
} }
}, },
@@ -805,6 +805,7 @@
"apply": "Uygula", "apply": "Uygula",
"backToOverview": "Genel bakışa dön", "backToOverview": "Genel bakışa dön",
"create": "Oluştur", "create": "Oluştur",
"createAnother": "Kaydet ve Yeni Oluştur",
"edit": "Düzenle", "edit": "Düzenle",
"import": "İçe aktar", "import": "İçe aktar",
"insert": "Ekle", "insert": "Ekle",
@@ -946,7 +947,8 @@
"moveUp": "Yukarı taşı", "moveUp": "Yukarı taşı",
"moveDown": "Aşağı taşı", "moveDown": "Aşağı taşı",
"createAbove": "Yukarı Yeni Kategori", "createAbove": "Yukarı Yeni Kategori",
"createBelow": "Aşağı Yeni Kategori" "createBelow": "Aşağı Yeni Kategori",
"openAllInNewTabs": "Tümünü sekmelerde aç"
}, },
"create": { "create": {
"title": "Yeni Kategori", "title": "Yeni Kategori",
@@ -965,6 +967,10 @@
"create": "Yeni Kategori", "create": "Yeni Kategori",
"changePosition": "Pozisyonu değiştir" "changePosition": "Pozisyonu değiştir"
} }
},
"openAllInNewTabs": {
"title": "Tümünü sekmelerde aç",
"text": "Bazı tarayıcılar güvenlik nedeniyle sekmelerin toplu olarak açılmasını engelleyebilir. Homarr tüm pencereleri açamadı, çünkü tarayıcınız bu eylemi engelledi. Lütfen \"Açılır pencereleri aç\"a izin verin ve tekrar deneyin."
} }
} }
}, },
@@ -1166,21 +1172,21 @@
"description": "Geçerli tarih ve saati görüntüler.", "description": "Geçerli tarih ve saati görüntüler.",
"option": { "option": {
"customTitleToggle": { "customTitleToggle": {
"label": "Özel Başlık/Şehir gösterimi", "label": "Özel Başlık/Şehir göster",
"description": "Saatin üstüne özel bir başlık veya şehir/ülke adını ekleyin." "description": "Saatin üstüne özel bir başlık veya şehir/ülke adı ekleminizi sağlar."
}, },
"customTitle": { "customTitle": {
"label": "Başlık" "label": "Başlık"
}, },
"is24HourFormat": { "is24HourFormat": {
"label": "24 saatlik format", "label": "24 saatlik biçim",
"description": "12 saatlik format yerine 24 saatlik formatı kullanın" "description": "12 saatlik format yerine 24 saatlik biçimi kullanın"
}, },
"showSeconds": { "showSeconds": {
"label": "Saniyeleri görüntüle" "label": "Saniyeleri göster"
}, },
"useCustomTimezone": { "useCustomTimezone": {
"label": "Sabit bir zaman dilimi kullanın" "label": "Sabit zaman dilimi kullan"
}, },
"timezone": { "timezone": {
"label": "Saat dilimi", "label": "Saat dilimi",
@@ -1191,7 +1197,15 @@
}, },
"dateFormat": { "dateFormat": {
"label": "Tarih Biçimi", "label": "Tarih Biçimi",
"description": "Tarihin nasıl görüneceği" "description": "Görüntülenecek tarihin biçimi"
},
"customTimeFormat": {
"label": "Özel zaman biçimi",
"description": "Zaman biçimini değiştirmek için ISO 8601 standardını kullanın (bu diğer seçenekleri geçersiz kılacaktır)"
},
"customDateFormat": {
"label": "Özel tarih biçimi",
"description": "Tarih biçimini değiştirmek için ISO 8601 standardını kullanın (bu diğer seçenekleri geçersiz kılacaktır)"
} }
} }
}, },
@@ -1354,8 +1368,8 @@
"label": "Radarr yayın türü", "label": "Radarr yayın türü",
"options": { "options": {
"inCinemas": "Sinemalarda", "inCinemas": "Sinemalarda",
"digitalRelease": "Dijital sürüm", "digitalRelease": "Dijital Yayın",
"physicalRelease": "Fiziksel Yayınlanan" "physicalRelease": "Fiziksel Yayın"
} }
}, },
"filterPastMonths": { "filterPastMonths": {
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Fahrenheit cinsinden sıcaklık" "label": "Fahrenheit cinsinden sıcaklık"
}, },
"disableTemperatureDecimals": {
"label": "Sıcaklıklarda ondalıkları gösterme"
},
"showCurrentWindSpeed": {
"label": "Mevcut rüzgar hızını göster",
"description": "Yalnızca mevcut hava durumunda"
},
"location": { "location": {
"label": "Hava durumu konumu" "label": "Hava durumu konumu"
}, },
@@ -1388,23 +1409,30 @@
}, },
"dateFormat": { "dateFormat": {
"label": "Tarih Biçimi", "label": "Tarih Biçimi",
"description": "Tarihin nasıl görüneceği" "description": "Görüntülenecek tarihin biçimi"
} }
}, },
"currentWindSpeed": "{currentWindSpeed} km/s",
"dailyForecast": {
"sunrise": "Gün Doğumu",
"sunset": "Gün Batımı",
"maxWindSpeed": "Maks. Rüzgar Hızı: {maxWindSpeed} km/s",
"maxWindGusts": "Maks. Rüzgar Hamlesi: {maxWindGusts} km/s"
},
"kind": { "kind": {
"clear": "Temiz", "clear": "Açık",
"mainlyClear": "Genel olarak açık", "mainlyClear": "Genel olarak açık",
"fog": "Sis", "fog": "Sisli",
"drizzle": "Çiseleme", "drizzle": "Çiseleme",
"freezingDrizzle": "Soğuk çiseleme", "freezingDrizzle": "Donan Çisenti",
"rain": "Yağmur", "rain": "Yağmur",
"freezingRain": "Dondurucu yağmur", "freezingRain": "Donan Yağmur",
"snowFall": "Kar yağışı", "snowFall": "Kar Yağış",
"snowGrains": "Kar taneleri", "snowGrains": "Donmuş Kar",
"rainShowers": "Sağanak yağmur", "rainShowers": "Sağanak Yağmur",
"snowShowers": "Kar yağışı", "snowShowers": "Kar Yağışlı",
"thunderstorm": "Fırtına", "thunderstorm": "Fırtına",
"thunderstormWithHail": "Fırtına ve dolu", "thunderstormWithHail": "Dolu Yağışlı Fırtına",
"unknown": "Bilinmeyen" "unknown": "Bilinmeyen"
} }
}, },
@@ -1696,7 +1724,7 @@
"completed": "Tamamlanan", "completed": "Tamamlanan",
"failed": "Başarısız", "failed": "Başarısız",
"processing": "İşlemde", "processing": "İşlemde",
"leeching": "Leechleme", "leeching": "Aranıyor",
"stalled": "Durduruldu", "stalled": "Durduruldu",
"unknown": "Bilinmeyen", "unknown": "Bilinmeyen",
"seeding": "Seed ediliyor" "seeding": "Seed ediliyor"
@@ -2134,8 +2162,8 @@
"homeBoard": { "homeBoard": {
"title": "Öntanımlı Panel Yapılandırılmadı", "title": "Öntanımlı Panel Yapılandırılmadı",
"admin": { "admin": {
"description": "Sunucu için henüz bir ana sayfa paneli belirlemediniz.", "description": "Sunucu için henüz öntanımlı panel belirlemediniz.",
"link": "Sunucu geneli için ana panel yapılandırın", "link": "Sunucu geneli için öntanımlı panel atayın",
"notice": "Bu sayfayı tüm kullanıcılardan gizlemek için sunucu için bir ana panel ayarlayın" "notice": "Bu sayfayı tüm kullanıcılardan gizlemek için sunucu için bir ana panel ayarlayın"
}, },
"user": { "user": {
@@ -2287,7 +2315,7 @@
"mobile": "Mobil" "mobile": "Mobil"
} }
}, },
"search": "", "search": "Ara",
"firstDayOfWeek": "Haftanın ilk günü", "firstDayOfWeek": "Haftanın ilk günü",
"accessibility": "Erişilebilirlik" "accessibility": "Erişilebilirlik"
} }
@@ -2445,8 +2473,8 @@
"board": { "board": {
"title": "Paneller", "title": "Paneller",
"homeBoard": { "homeBoard": {
"label": "Genel Ana Sayfa Paneli", "label": "Genel öntanımlı panel",
"mobileLabel": "Genel Mobil Panel", "mobileLabel": "Genel öntanımlı mobil panel",
"description": "Seçim yalnızca herkese açık paneller için kullanılabilir" "description": "Seçim yalnızca herkese açık paneller için kullanılabilir"
} }
}, },
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "API token'ı oluştur" "createApiToken": "API token'ı oluştur"
}, },
"modal": {
"delete": {
"title": "API belirtecini sil",
"text": "Bu, API belirtecini kalıcı olarak silecektir. Bu belirteci kullanan API istemcileri artık kimlik doğrulaması yapamaz ve API istekleri gerçekleştiremez. Bu eylem geri alınamaz."
}
},
"table": { "table": {
"header": { "header": {
"id": "Kimlik", "id": "Kimlik",
"createdBy": "Tarafından oluşturuldu" "createdBy": "Tarafından oluşturuldu",
"actions": "Eylemler"
} }
} }
} }
+156 -121
View File
@@ -51,7 +51,7 @@
"title": "Введіть токен імпорту", "title": "Введіть токен імпорту",
"field": { "field": {
"token": { "token": {
"label": "Токен.", "label": "Токен",
"description": "Введіть показаний токен імпорту з вашого попереднього екземпляра homarr" "description": "Введіть показаний токен імпорту з вашого попереднього екземпляра homarr"
} }
}, },
@@ -153,10 +153,10 @@
"label": "Використовувати значки для ping" "label": "Використовувати значки для ping"
}, },
"defaultSearchEngine": { "defaultSearchEngine": {
"label": "" "label": "Стандартна система пошуку"
}, },
"openSearchInNewTab": { "openSearchInNewTab": {
"label": "" "label": "Відкрити результати пошуку в новій вкладці"
} }
}, },
"error": { "error": {
@@ -219,10 +219,10 @@
"changeSearchPreferences": { "changeSearchPreferences": {
"notification": { "notification": {
"success": { "success": {
"message": "" "message": "Налаштування пошуку успішно змінено"
}, },
"error": { "error": {
"message": "" "message": "Не вдалося змінити налаштування пошуку"
} }
} }
}, },
@@ -805,6 +805,7 @@
"apply": "Застосувати", "apply": "Застосувати",
"backToOverview": "Назад до огляду", "backToOverview": "Назад до огляду",
"create": "Створити", "create": "Створити",
"createAnother": "",
"edit": "Редагувати", "edit": "Редагувати",
"import": "Імпорт", "import": "Імпорт",
"insert": "Вставити", "insert": "Вставити",
@@ -877,7 +878,7 @@
"switchToDarkMode": "Перемикнути на темну тему", "switchToDarkMode": "Перемикнути на темну тему",
"switchToLightMode": "Перемикнути на світлу тему", "switchToLightMode": "Перемикнути на світлу тему",
"management": "Управління", "management": "Управління",
"preferences": "Ваші уподобання", "preferences": "Ваші налаштування",
"logout": "Вийти", "logout": "Вийти",
"login": "Логін", "login": "Логін",
"homeBoard": "Ваша домашня дошка", "homeBoard": "Ваша домашня дошка",
@@ -946,7 +947,8 @@
"moveUp": "Перемістити вгору", "moveUp": "Перемістити вгору",
"moveDown": "Перемістити вниз", "moveDown": "Перемістити вниз",
"createAbove": "Нові категорії зверху", "createAbove": "Нові категорії зверху",
"createBelow": "Нова категорія знизу" "createBelow": "Нова категорія знизу",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "Нова категорія", "title": "Нова категорія",
@@ -965,6 +967,10 @@
"create": "Нова категорія", "create": "Нова категорія",
"changePosition": "Змінити положення" "changePosition": "Змінити положення"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "Формат дати", "label": "Формат дати",
"description": "Як має виглядати дата" "description": "Як має виглядати дата"
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "Температура за Фаренгейтом" "label": "Температура за Фаренгейтом"
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Погодна локація" "label": "Погодна локація"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Ясно", "clear": "Ясно",
"mainlyClear": "Здебільшого ясно", "mainlyClear": "Здебільшого ясно",
@@ -1410,10 +1438,10 @@
}, },
"indexerManager": { "indexerManager": {
"name": "Статус менеджера індексування", "name": "Статус менеджера індексування",
"description": "", "description": "Статус ваших індексаторів",
"option": { "option": {
"openIndexerSiteInNewTab": { "openIndexerSiteInNewTab": {
"label": "" "label": "Відкрити сайт індексатора у новій вкладці"
} }
}, },
"title": "Менеджер індексації", "title": "Менеджер індексації",
@@ -1575,8 +1603,8 @@
} }
}, },
"mediaServer": { "mediaServer": {
"name": "", "name": "Поточні потоки медіасервера",
"description": "", "description": "Показує поточні потоки з ваших медіасерверів",
"option": {}, "option": {},
"items": { "items": {
"user": "Користувач", "user": "Користувач",
@@ -1674,7 +1702,7 @@
}, },
"state": { "state": {
"columnTitle": "Стан", "columnTitle": "Стан",
"detailsTitle": "" "detailsTitle": "Стан"
}, },
"time": { "time": {
"columnTitle": "Час завершення", "columnTitle": "Час завершення",
@@ -1682,7 +1710,7 @@
}, },
"type": { "type": {
"columnTitle": "Тип", "columnTitle": "Тип",
"detailsTitle": "" "detailsTitle": "Тип клієнта"
}, },
"upSpeed": { "upSpeed": {
"columnTitle": "Віддача", "columnTitle": "Віддача",
@@ -1696,10 +1724,10 @@
"completed": "Завершено", "completed": "Завершено",
"failed": "Невдало", "failed": "Невдало",
"processing": "В обробці", "processing": "В обробці",
"leeching": "", "leeching": "Завантажується",
"stalled": "", "stalled": "",
"unknown": "Невідомо", "unknown": "Невідомо",
"seeding": "" "seeding": "Роздається"
}, },
"actions": { "actions": {
"clients": { "clients": {
@@ -1828,7 +1856,7 @@
} }
}, },
"rssFeed": { "rssFeed": {
"name": "", "name": "RSS-канали",
"description": "", "description": "",
"option": { "option": {
"feedUrls": { "feedUrls": {
@@ -2058,14 +2086,14 @@
"title": "" "title": ""
}, },
"access": { "access": {
"title": "", "title": "Управління доступом",
"permission": { "permission": {
"item": { "item": {
"view": { "view": {
"label": "Перегляд дошки" "label": "Перегляд дошки"
}, },
"modify": { "modify": {
"label": "" "label": "Змінювати дошку"
}, },
"full": { "full": {
"label": "Повний доступ" "label": "Повний доступ"
@@ -2079,20 +2107,20 @@
"rename": { "rename": {
"label": "Перейменувати дошку", "label": "Перейменувати дошку",
"description": "", "description": "",
"button": "", "button": "Змінити назву",
"modal": { "modal": {
"title": "" "title": "Перейменувати дошку"
} }
}, },
"visibility": { "visibility": {
"label": "", "label": "Змінити видимість дошки",
"description": { "description": {
"public": "", "public": "Ця дошка є загальнодоступною.",
"private": "" "private": "Ця дошка є приватною."
}, },
"button": { "button": {
"public": "", "public": "Зробити приватною",
"private": "" "private": "Зробити загальнодоступною"
}, },
"confirm": { "confirm": {
"public": { "public": {
@@ -2281,13 +2309,13 @@
"item": { "item": {
"language": "Мова та Регіон", "language": "Мова та Регіон",
"board": { "board": {
"title": "", "title": "Домашня дошка",
"type": { "type": {
"general": "", "general": "Загальна",
"mobile": "" "mobile": "Мобільна"
} }
}, },
"search": "", "search": "Пошук",
"firstDayOfWeek": "Перший день тижня", "firstDayOfWeek": "Перший день тижня",
"accessibility": "Доступність" "accessibility": "Доступність"
} }
@@ -2304,11 +2332,11 @@
"title": "Користувачі" "title": "Користувачі"
}, },
"edit": { "edit": {
"metaTitle": "" "metaTitle": "Редагувати користувача {username}"
}, },
"create": { "create": {
"metaTitle": "Створити користувача", "metaTitle": "Створити користувача",
"title": "", "title": "Створити нового користувача",
"step": { "step": {
"personalInformation": { "personalInformation": {
"label": "" "label": ""
@@ -2317,23 +2345,23 @@
"label": "Безпека" "label": "Безпека"
}, },
"groups": { "groups": {
"label": "", "label": "Групи",
"title": "", "title": "Виберіть усі групи, учасником яких має бути користувач",
"description": "" "description": ""
}, },
"review": { "review": {
"label": "" "label": "Огляд"
}, },
"completed": { "completed": {
"title": "" "title": "Користувача створено"
}, },
"error": { "error": {
"title": "" "title": "Не вдалося створити користувача"
} }
}, },
"action": { "action": {
"createAnother": "", "createAnother": "Створити іншого користувача",
"back": "" "back": "Повернутися до списку користувачів"
} }
}, },
"invite": { "invite": {
@@ -2371,23 +2399,23 @@
} }
}, },
"group": { "group": {
"back": "", "back": "Назад до груп",
"setting": { "setting": {
"general": { "general": {
"title": "Загальне", "title": "Загальне",
"owner": "Власник", "owner": "Власник",
"ownerOfGroup": "", "ownerOfGroup": "Власник цієї групи",
"ownerOfGroupDeleted": "" "ownerOfGroupDeleted": "Власника цієї групи видалено. Наразі не має власника."
}, },
"members": { "members": {
"title": "", "title": "Учасники",
"search": "", "search": "Знайти учасника",
"notFound": "" "notFound": "Учасників не знайдено"
}, },
"permissions": { "permissions": {
"title": "", "title": "Дозволи",
"form": { "form": {
"unsavedChanges": "" "unsavedChanges": "У вас є незбережені зміни!"
} }
} }
} }
@@ -2477,7 +2505,7 @@
}, },
"tool": { "tool": {
"tasks": { "tasks": {
"title": "", "title": "Завдання",
"status": { "status": {
"idle": "", "idle": "",
"running": "Запущено", "running": "Запущено",
@@ -2485,13 +2513,13 @@
}, },
"job": { "job": {
"minecraftServerStatus": { "minecraftServerStatus": {
"label": "" "label": "Статус сервера Minecraft"
}, },
"iconsUpdater": { "iconsUpdater": {
"label": "" "label": ""
}, },
"analytics": { "analytics": {
"label": "" "label": "Аналітика"
}, },
"smartHomeEntityState": { "smartHomeEntityState": {
"label": "" "label": ""
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "Створити API токен" "createApiToken": "Створити API токен"
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "ІДЕНТИФІКАТОР", "id": "ІДЕНТИФІКАТОР",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
@@ -2591,7 +2626,7 @@
"table": { "table": {
"updated": "Оновлено {when}", "updated": "Оновлено {when}",
"search": "", "search": "",
"selected": "" "selected": "Вибрано {selectCount} із {totalCount} контейнерів"
}, },
"field": { "field": {
"name": { "name": {
@@ -2706,10 +2741,10 @@
"permission": { "permission": {
"title": "Дозволи", "title": "Дозволи",
"userSelect": { "userSelect": {
"title": "" "title": "Додати дозвіл для користувача"
}, },
"groupSelect": { "groupSelect": {
"title": "" "title": "Додати дозвіл для групи"
}, },
"tab": { "tab": {
"user": "Користувачі", "user": "Користувачі",
@@ -2724,12 +2759,12 @@
"label": "Група" "label": "Група"
}, },
"permission": { "permission": {
"label": "" "label": "Дозвіл"
} }
}, },
"action": { "action": {
"saveUser": "", "saveUser": "Зберегти дозвіл користувача",
"saveGroup": "" "saveGroup": "Зберегти дозвіл групи"
} }
}, },
"navigationStructure": { "navigationStructure": {
@@ -2739,7 +2774,7 @@
"label": "Дошки" "label": "Дошки"
}, },
"integrations": { "integrations": {
"label": "", "label": "Інтеграції",
"edit": { "edit": {
"label": "Редагувати" "label": "Редагувати"
}, },
@@ -2748,7 +2783,7 @@
} }
}, },
"search-engines": { "search-engines": {
"label": "", "label": "Пошукові системи",
"new": { "new": {
"label": "" "label": ""
}, },
@@ -2757,7 +2792,7 @@
} }
}, },
"medias": { "medias": {
"label": "" "label": "Медіа"
}, },
"apps": { "apps": {
"label": "Додатки", "label": "Додатки",
@@ -2777,7 +2812,7 @@
"security": "Безпека", "security": "Безпека",
"board": "Дошки", "board": "Дошки",
"groups": { "groups": {
"label": "" "label": "Групи"
}, },
"invites": { "invites": {
"label": "Запрошення" "label": "Запрошення"
@@ -2786,13 +2821,13 @@
"tools": { "tools": {
"label": "Інструменти", "label": "Інструменти",
"docker": { "docker": {
"label": "" "label": "Докер"
}, },
"logs": { "logs": {
"label": "" "label": "Логи"
}, },
"certificates": { "certificates": {
"label": "" "label": "Сертифікати"
} }
}, },
"settings": { "settings": {
@@ -2805,7 +2840,7 @@
}, },
"search": { "search": {
"placeholder": "", "placeholder": "",
"nothingFound": "", "nothingFound": "Нічого не знайдено",
"error": { "error": {
"fetch": "" "fetch": ""
}, },
@@ -2821,7 +2856,7 @@
"label": "Відкрити посилання на додаток" "label": "Відкрити посилання на додаток"
}, },
"edit": { "edit": {
"label": "" "label": "Редагувати додаток"
} }
}, },
"detail": { "detail": {
@@ -2837,10 +2872,10 @@
"label": "Відкрити дошку" "label": "Відкрити дошку"
}, },
"homeBoard": { "homeBoard": {
"label": "" "label": "Встановити як домашню дошку"
}, },
"mobileBoard": { "mobileBoard": {
"label": "" "label": "Встановити як мобільну дошку"
}, },
"settings": { "settings": {
"label": "Відкрити налаштування" "label": "Відкрити налаштування"
@@ -2852,25 +2887,25 @@
} }
}, },
"integration": { "integration": {
"title": "" "title": "Інтеграції"
} }
} }
}, },
"command": { "command": {
"help": "", "help": "Активувати командний режим",
"group": { "group": {
"localCommand": { "localCommand": {
"title": "" "title": "Локальні команди"
}, },
"globalCommand": { "globalCommand": {
"title": "", "title": "",
"option": { "option": {
"colorScheme": { "colorScheme": {
"light": "", "light": "Перемикнути на світлу тему",
"dark": "" "dark": "Перемикнути на темну тему"
}, },
"language": { "language": {
"label": "", "label": "Змінити мову",
"children": { "children": {
"detail": { "detail": {
"title": "" "title": ""
@@ -2881,53 +2916,53 @@
"label": "Створити нову дошку" "label": "Створити нову дошку"
}, },
"importBoard": { "importBoard": {
"label": "" "label": "Імпортувати дошку"
}, },
"newApp": { "newApp": {
"label": "" "label": "Створити новий додаток"
}, },
"newIntegration": { "newIntegration": {
"label": "", "label": "Створити нову інтеграцію",
"children": { "children": {
"detail": { "detail": {
"title": "" "title": "Виберіть тип інтеграції, який потрібно створити"
} }
} }
}, },
"newUser": { "newUser": {
"label": "" "label": "Створити нового користувача"
}, },
"newInvite": { "newInvite": {
"label": "Створити нове запрошення" "label": "Створити нове запрошення"
}, },
"newGroup": { "newGroup": {
"label": "" "label": "Створити нову групу"
} }
} }
} }
} }
}, },
"media": { "media": {
"requestMovie": "", "requestMovie": "Запросити фільм",
"requestSeries": "", "requestSeries": "Запросити серіал",
"openIn": "" "openIn": "Відкрити в {kind}"
}, },
"external": { "external": {
"help": "Використовувати зовнішню пошукову систему", "help": "Використовувати зовнішню пошукову систему",
"group": { "group": {
"searchEngine": { "searchEngine": {
"title": "", "title": "Пошукові системи",
"children": { "children": {
"action": { "action": {
"search": { "search": {
"label": "" "label": "Шукати з {name}"
} }
}, },
"detail": { "detail": {
"title": "" "title": "Виберіть дію для пошукової системи"
}, },
"searchResults": { "searchResults": {
"title": "" "title": "Виберіть результат пошуку для дій"
} }
}, },
"option": { "option": {
@@ -3053,7 +3088,7 @@
"label": "Про програму" "label": "Про програму"
}, },
"homeBoard": { "homeBoard": {
"label": "" "label": "Домашня дошка"
}, },
"preferences": { "preferences": {
"label": "Ваші уподобання" "label": "Ваші уподобання"
@@ -3126,47 +3161,47 @@
"interactive": "" "interactive": ""
}, },
"create": { "create": {
"title": "", "title": "Нова пошукова система",
"notification": { "notification": {
"success": { "success": {
"title": "", "title": "Пошукова система створена",
"message": "" "message": "Пошукова система успішно створена"
}, },
"error": { "error": {
"title": "", "title": "Пошукова система не створена",
"message": "" "message": "Не вдалося створити пошукову систему"
} }
} }
}, },
"edit": { "edit": {
"title": "", "title": "Редагувати систему пошуку",
"notification": { "notification": {
"success": { "success": {
"title": "", "title": "Зміни успішно застосовано",
"message": "" "message": "Пошукову систему успішно збережено"
}, },
"error": { "error": {
"title": "", "title": "Не вдалося застосувати зміни",
"message": "" "message": "Не вдалося зберегти пошукову систему"
} }
}, },
"configControl": "", "configControl": "Конфігурація",
"searchEngineType": { "searchEngineType": {
"generic": "", "generic": "",
"fromIntegration": "" "fromIntegration": "З інтеграції"
} }
}, },
"delete": { "delete": {
"title": "", "title": "Видалити пошукову систему",
"message": "", "message": "Ви впевнені, що хочете видалити пошукову систему \"{name}\"?",
"notification": { "notification": {
"success": { "success": {
"title": "", "title": "Пошукову систему видалено",
"message": "" "message": "Пошукову систему успішно видалено"
}, },
"error": { "error": {
"title": "", "title": "Пошукова система не видалена",
"message": "" "message": "Не вдалося видалити пошукову систему"
} }
} }
} }
@@ -3174,15 +3209,15 @@
"media": { "media": {
"request": { "request": {
"modal": { "modal": {
"title": "", "title": "Запит \"{name}\"",
"table": { "table": {
"header": { "header": {
"season": "", "season": "Сезон",
"episodes": "" "episodes": "Серії"
} }
}, },
"button": { "button": {
"send": "" "send": "Надіслати запит"
} }
} }
} }
@@ -3195,36 +3230,36 @@
"title": "", "title": "",
"description": "", "description": "",
"noResults": { "noResults": {
"title": "" "title": "Сертифікати відсутні"
}, },
"expires": "" "expires": ""
} }
}, },
"action": { "action": {
"create": { "create": {
"label": "", "label": "Додати сертифікат",
"notification": { "notification": {
"success": { "success": {
"title": "", "title": "Додано сертифікат",
"message": "" "message": "Сертифікат було успішно додано"
}, },
"error": { "error": {
"title": "", "title": "Не вдалося додати сертифікат",
"message": "" "message": "Не вдалося додати сертифікат"
} }
} }
}, },
"remove": { "remove": {
"label": "", "label": "Видалити сертифікат",
"confirm": "", "confirm": "Ви впевнені, що хочете видалити сертифікат?",
"notification": { "notification": {
"success": { "success": {
"title": "", "title": "Сертифікат видалено",
"message": "" "message": "Сертифікат успішно видалено"
}, },
"error": { "error": {
"title": "", "title": "Сертификат не видалено",
"message": "" "message": "Не вдалося видалити сертифікат"
} }
} }
} }
+37 -2
View File
@@ -805,6 +805,7 @@
"apply": "Áp dụng", "apply": "Áp dụng",
"backToOverview": "", "backToOverview": "",
"create": "Tạo nên", "create": "Tạo nên",
"createAnother": "",
"edit": "Sửa", "edit": "Sửa",
"import": "", "import": "",
"insert": "Thêm", "insert": "Thêm",
@@ -946,7 +947,8 @@
"moveUp": "Đi lên", "moveUp": "Đi lên",
"moveDown": "Đi xuống", "moveDown": "Đi xuống",
"createAbove": "", "createAbove": "",
"createBelow": "" "createBelow": "",
"openAllInNewTabs": ""
}, },
"create": { "create": {
"title": "", "title": "",
@@ -965,6 +967,10 @@
"create": "", "create": "",
"changePosition": "Đổi vị trí" "changePosition": "Đổi vị trí"
} }
},
"openAllInNewTabs": {
"title": "",
"text": ""
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "", "label": "",
"description": "" "description": ""
},
"customTimeFormat": {
"label": "",
"description": ""
},
"customDateFormat": {
"label": "",
"description": ""
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "" "label": ""
}, },
"disableTemperatureDecimals": {
"label": ""
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
},
"location": { "location": {
"label": "Vị trí thời tiết" "label": "Vị trí thời tiết"
}, },
@@ -1391,6 +1412,13 @@
"description": "" "description": ""
} }
}, },
"currentWindSpeed": "",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
},
"kind": { "kind": {
"clear": "Quang đãng", "clear": "Quang đãng",
"mainlyClear": "Thông thoáng", "mainlyClear": "Thông thoáng",
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "" "createApiToken": ""
}, },
"modal": {
"delete": {
"title": "",
"text": ""
}
},
"table": { "table": {
"header": { "header": {
"id": "NHẬN DẠNG", "id": "NHẬN DẠNG",
"createdBy": "" "createdBy": "",
"actions": ""
} }
} }
} }
+44 -9
View File
@@ -153,10 +153,10 @@
"label": "為 Pings 使用 icon" "label": "為 Pings 使用 icon"
}, },
"defaultSearchEngine": { "defaultSearchEngine": {
"label": "" "label": "預設搜尋引擎"
}, },
"openSearchInNewTab": { "openSearchInNewTab": {
"label": "" "label": "於新分頁中開啟搜尋結果"
} }
}, },
"error": { "error": {
@@ -219,10 +219,10 @@
"changeSearchPreferences": { "changeSearchPreferences": {
"notification": { "notification": {
"success": { "success": {
"message": "" "message": "搜尋設定更改成功"
}, },
"error": { "error": {
"message": "" "message": "無法更改搜尋設定"
} }
} }
}, },
@@ -805,6 +805,7 @@
"apply": "應用", "apply": "應用",
"backToOverview": "返回總覽", "backToOverview": "返回總覽",
"create": "創建", "create": "創建",
"createAnother": "創建並重新開始",
"edit": "編輯", "edit": "編輯",
"import": "匯入", "import": "匯入",
"insert": "插入", "insert": "插入",
@@ -946,7 +947,8 @@
"moveUp": "上移", "moveUp": "上移",
"moveDown": "下移", "moveDown": "下移",
"createAbove": "新分類之上", "createAbove": "新分類之上",
"createBelow": "新分類之下" "createBelow": "新分類之下",
"openAllInNewTabs": "於分頁中開啟全部"
}, },
"create": { "create": {
"title": "創建分類", "title": "創建分類",
@@ -965,6 +967,10 @@
"create": "創建分類", "create": "創建分類",
"changePosition": "變更位置" "changePosition": "變更位置"
} }
},
"openAllInNewTabs": {
"title": "於分頁中開啟全部",
"text": "某些瀏覽器可能會因安全性考量而阻止批量開啟分頁,Homarr 無法開啟所有視窗,因為您的瀏覽器已封鎖此操作,請允許「開啟彈出式視窗」,然後重試"
} }
} }
}, },
@@ -1192,6 +1198,14 @@
"dateFormat": { "dateFormat": {
"label": "日期格式", "label": "日期格式",
"description": "日期應該是什麼樣式" "description": "日期應該是什麼樣式"
},
"customTimeFormat": {
"label": "自訂時間格式",
"description": "使用 ISO 8601 格式化時間 (此操作將覆蓋其他選項)"
},
"customDateFormat": {
"label": "自訂日期格式",
"description": "使用 ISO 8601 格式化日期 (此操作將覆蓋其他選項)"
} }
} }
}, },
@@ -1373,6 +1387,13 @@
"isFormatFahrenheit": { "isFormatFahrenheit": {
"label": "華氏溫度" "label": "華氏溫度"
}, },
"disableTemperatureDecimals": {
"label": "關閉溫度小數點"
},
"showCurrentWindSpeed": {
"label": "顯示當前風速",
"description": "僅限當前天氣"
},
"location": { "location": {
"label": "天氣位置" "label": "天氣位置"
}, },
@@ -1391,6 +1412,13 @@
"description": "日期應該是什麼樣式" "description": "日期應該是什麼樣式"
} }
}, },
"currentWindSpeed": "{currentWindSpeed} 公里/時",
"dailyForecast": {
"sunrise": "日出",
"sunset": "日落",
"maxWindSpeed": "最大風速:{maxWindSpeed} 公里/時",
"maxWindGusts": "最大陣風:{maxWindGusts} 公里/時"
},
"kind": { "kind": {
"clear": "晴朗", "clear": "晴朗",
"mainlyClear": "大多晴朗", "mainlyClear": "大多晴朗",
@@ -1426,8 +1454,8 @@
"name": "系統健康監測", "name": "系統健康監測",
"description": "顯示系統運行狀態", "description": "顯示系統運行狀態",
"tab": { "tab": {
"system": "", "system": "系統",
"cluster": "" "cluster": "叢集"
}, },
"option": { "option": {
"fahrenheit": { "fahrenheit": {
@@ -2287,7 +2315,7 @@
"mobile": "移動裝置" "mobile": "移動裝置"
} }
}, },
"search": "", "search": "搜尋",
"firstDayOfWeek": "一周的第一天", "firstDayOfWeek": "一周的第一天",
"accessibility": "無障礙服務" "accessibility": "無障礙服務"
} }
@@ -2556,10 +2584,17 @@
"button": { "button": {
"createApiToken": "創建 API 密鑰" "createApiToken": "創建 API 密鑰"
}, },
"modal": {
"delete": {
"title": "刪除 API 密鑰",
"text": "這將永久刪除 API 金鑰,使用此金鑰的 API 客戶端將無法再進行身份驗證或執行 API 請求,此操作無法撤銷"
}
},
"table": { "table": {
"header": { "header": {
"id": "ID", "id": "ID",
"createdBy": "創建者" "createdBy": "創建者",
"actions": "動作"
} }
} }
} }
+2 -1
View File
@@ -1,5 +1,6 @@
import type { Icon123 } from "@tabler/icons-react"; import type { Icon123, IconProps } from "@tabler/icons-react";
export * from "./src"; export * from "./src";
export type TablerIcon = typeof Icon123; export type TablerIcon = typeof Icon123;
export type TablerIconProps = IconProps;
+1 -1
View File
@@ -32,7 +32,7 @@
"@mantine/core": "^7.16.2", "@mantine/core": "^7.16.2",
"@mantine/dates": "^7.16.2", "@mantine/dates": "^7.16.2",
"@mantine/hooks": "^7.16.2", "@mantine/hooks": "^7.16.2",
"@tabler/icons-react": "^3.29.0", "@tabler/icons-react": "^3.30.0",
"mantine-react-table": "2.0.0-beta.8", "mantine-react-table": "2.0.0-beta.8",
"next": "15.1.6", "next": "15.1.6",
"react": "19.0.0", "react": "19.0.0",
@@ -1,4 +1,4 @@
import type { MantineSize } from "@mantine/core"; import type { MantineRadius, MantineSize } from "@mantine/core";
import { Avatar } from "@mantine/core"; import { Avatar } from "@mantine/core";
import type { IntegrationKind } from "@homarr/definitions"; import type { IntegrationKind } from "@homarr/definitions";
@@ -7,13 +7,14 @@ import { getIconUrl } from "@homarr/definitions";
interface IntegrationAvatarProps { interface IntegrationAvatarProps {
size: MantineSize; size: MantineSize;
kind: IntegrationKind | null; kind: IntegrationKind | null;
radius?: MantineRadius;
} }
export const IntegrationAvatar = ({ kind, size }: IntegrationAvatarProps) => { export const IntegrationAvatar = ({ kind, size, radius }: IntegrationAvatarProps) => {
const url = kind ? getIconUrl(kind) : null; const url = kind ? getIconUrl(kind) : null;
if (!url) { if (!url) {
return null; return null;
} }
return <Avatar size={size} src={url} />; return <Avatar size={size} src={url} radius={radius} styles={{ image: { objectFit: "contain" } }} />;
}; };
+3 -1
View File
@@ -10,9 +10,10 @@ import { IconSearch } from "@tabler/icons-react";
interface SearchInputProps { interface SearchInputProps {
defaultValue?: string; defaultValue?: string;
placeholder: string; placeholder: string;
flexExpand?: boolean;
} }
export const SearchInput = ({ placeholder, defaultValue }: SearchInputProps) => { export const SearchInput = ({ placeholder, defaultValue, flexExpand = false }: SearchInputProps) => {
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const { replace } = useRouter(); const { replace } = useRouter();
const pathName = usePathname(); const pathName = usePathname();
@@ -40,6 +41,7 @@ export const SearchInput = ({ placeholder, defaultValue }: SearchInputProps) =>
defaultValue={defaultValue} defaultValue={defaultValue}
onChange={handleSearch} onChange={handleSearch}
placeholder={placeholder} placeholder={placeholder}
style={{ flex: flexExpand ? "1" : undefined }}
/> />
); );
}; };
@@ -9,12 +9,17 @@ export const atLocationOutput = z.object({
current_weather: z.object({ current_weather: z.object({
weathercode: z.number(), weathercode: z.number(),
temperature: z.number(), temperature: z.number(),
windspeed: z.number(),
}), }),
daily: z.object({ daily: z.object({
time: z.array(z.string()), time: z.array(z.string()),
weathercode: z.array(z.number()), weathercode: z.array(z.number()),
temperature_2m_max: z.array(z.number()), temperature_2m_max: z.array(z.number()),
temperature_2m_min: z.array(z.number()), temperature_2m_min: z.array(z.number()),
sunrise: z.array(z.string()),
sunset: z.array(z.string()),
wind_speed_10m_max: z.array(z.number()),
wind_gusts_10m_max: z.array(z.number()),
}), }),
}); });
+16 -16
View File
@@ -43,22 +43,22 @@
"@homarr/validation": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^7.16.2", "@mantine/core": "^7.16.2",
"@mantine/hooks": "^7.16.2", "@mantine/hooks": "^7.16.2",
"@tabler/icons-react": "^3.29.0", "@tabler/icons-react": "^3.30.0",
"@tiptap/extension-color": "2.11.3", "@tiptap/extension-color": "2.11.5",
"@tiptap/extension-highlight": "2.11.3", "@tiptap/extension-highlight": "2.11.5",
"@tiptap/extension-image": "2.11.3", "@tiptap/extension-image": "2.11.5",
"@tiptap/extension-link": "^2.11.3", "@tiptap/extension-link": "^2.11.5",
"@tiptap/extension-table": "2.11.3", "@tiptap/extension-table": "2.11.5",
"@tiptap/extension-table-cell": "2.11.3", "@tiptap/extension-table-cell": "2.11.5",
"@tiptap/extension-table-header": "2.11.3", "@tiptap/extension-table-header": "2.11.5",
"@tiptap/extension-table-row": "2.11.3", "@tiptap/extension-table-row": "2.11.5",
"@tiptap/extension-task-item": "2.11.3", "@tiptap/extension-task-item": "2.11.5",
"@tiptap/extension-task-list": "2.11.3", "@tiptap/extension-task-list": "2.11.5",
"@tiptap/extension-text-align": "2.11.3", "@tiptap/extension-text-align": "2.11.5",
"@tiptap/extension-text-style": "2.11.3", "@tiptap/extension-text-style": "2.11.5",
"@tiptap/extension-underline": "2.11.3", "@tiptap/extension-underline": "2.11.5",
"@tiptap/react": "^2.11.3", "@tiptap/react": "^2.11.5",
"@tiptap/starter-kit": "^2.11.3", "@tiptap/starter-kit": "^2.11.5",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"mantine-react-table": "2.0.0-beta.8", "mantine-react-table": "2.0.0-beta.8",
+8 -2
View File
@@ -17,6 +17,8 @@ export default function ClockWidget({ options }: WidgetComponentProps<"clock">)
const secondsFormat = options.showSeconds ? ":ss" : ""; const secondsFormat = options.showSeconds ? ":ss" : "";
const timeFormat = options.is24HourFormat ? `HH:mm${secondsFormat}` : `h:mm${secondsFormat} A`; const timeFormat = options.is24HourFormat ? `HH:mm${secondsFormat}` : `h:mm${secondsFormat} A`;
const dateFormat = options.dateFormat; const dateFormat = options.dateFormat;
const customTimeFormat = options.customTimeFormat;
const customDateFormat = options.customDateFormat;
const timezone = options.useCustomTimezone ? options.timezone : Intl.DateTimeFormat().resolvedOptions().timeZone; const timezone = options.useCustomTimezone ? options.timezone : Intl.DateTimeFormat().resolvedOptions().timeZone;
const time = useCurrentTime(options); const time = useCurrentTime(options);
return ( return (
@@ -27,11 +29,15 @@ export default function ClockWidget({ options }: WidgetComponentProps<"clock">)
</Text> </Text>
)} )}
<Text className="clock-time-text" fw={700} size="22.5cqmin" lh="1"> <Text className="clock-time-text" fw={700} size="22.5cqmin" lh="1">
{dayjs(time).tz(timezone).format(timeFormat)} {options.customTimeFormat
? dayjs(time).tz(timezone).format(customTimeFormat)
: dayjs(time).tz(timezone).format(timeFormat)}
</Text> </Text>
{options.showDate && ( {options.showDate && (
<Text className="clock-date-text" size="12.5cqmin" p="1cqmin" lineClamp={1}> <Text className="clock-date-text" size="12.5cqmin" p="1cqmin" lineClamp={1}>
{dayjs(time).tz(timezone).format(dateFormat)} {options.customDateFormat
? dayjs(time).tz(timezone).format(customDateFormat)
: dayjs(time).tz(timezone).format(dateFormat)}
</Text> </Text>
)} )}
</Stack> </Stack>
+8
View File
@@ -46,6 +46,14 @@ export const { definition, componentLoader } = createWidgetDefinition("clock", {
defaultValue: "dddd, MMMM D", defaultValue: "dddd, MMMM D",
withDescription: true, withDescription: true,
}), }),
customTimeFormat: factory.text({
defaultValue: "",
withDescription: true,
}),
customDateFormat: factory.text({
defaultValue: "",
withDescription: true,
}),
}), }),
{ {
customTitle: { customTitle: {
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more