+31
-4
@@ -1,11 +1,18 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"nav_homepage": "Homepage",
|
||||
"nav_download": "Download",
|
||||
"nav_packages": "Packages",
|
||||
"nav_wiki": "Wiki",
|
||||
"foot_social": "Social",
|
||||
"homepage_slogan": "Modern, fast and secure operating system.",
|
||||
"homepage_slogan_detailed": "SemiOS is a general purposed operating system with redesigned system components that strive to be fast, secure and flexible.\n\nIt is available for a variety of platforms, from desktop PCs to embedded boards.",
|
||||
"homepage_get": "Get SemiOS",
|
||||
"packages_search_title": "Search Packages",
|
||||
"packages_mirror_select": "Select an Mirror",
|
||||
"packages_repo_select": "Select an Repository",
|
||||
"packages_search_keyword": "Keyword",
|
||||
"packages_arch_select": "Architecture",
|
||||
"packages_arch_select": "Filtering Architecture",
|
||||
"packages_show_latest_only": "Show latest only",
|
||||
"packages_search_action": "Search",
|
||||
"packages_name": "Package Name",
|
||||
@@ -14,16 +21,36 @@
|
||||
"pkginfo_version": "Version",
|
||||
"pkginfo_arch": "Architecture",
|
||||
"pkginfo_installed_size": "Installed Size",
|
||||
"pkginfo_install_title": "Install Package",
|
||||
"pkginfo_install_download": "Download File",
|
||||
"pkginfo_install_packie_prompt_terminal": "To install this package via Packie in the default environment, execute the following command in the terminal:",
|
||||
"pkginfo_install_packie_pasted": "Command pasted!",
|
||||
"pkginfo_download_size": "Download Size",
|
||||
"pkginfo_download_title": "Download Package",
|
||||
"pkginfo_download_action": "Download",
|
||||
"pkginfo_download_filename": "Download Filename",
|
||||
"pkginfo_download_checksum": "File Checksum",
|
||||
"pkginfo_dependency_title": "Dependencies",
|
||||
"pkginfo_dependency_description": "This will list the dependencies and recommended packages for this package.",
|
||||
"pkginfo_dependency_pkgspec": "Package Specification",
|
||||
"pkginfo_dependency_type": "Type",
|
||||
"pkginfo_dependency_type_required": "Required",
|
||||
"pkginfo_dependency_type_recommended": "Recommended",
|
||||
"homepage_slogan": "The innovative operating system for the next generation of computing.",
|
||||
"homepage_get": "Get SemiOS"
|
||||
"pkginfo_maintainers": "Maintainers",
|
||||
"download_iso_image": "Get ISO Image",
|
||||
"download_iso_image_description": "Select and get the latest LiveCD images for general devices, like workstations and servers.",
|
||||
"download_iso_arch": "Architecture",
|
||||
"download_iso_image_ty": "Image Type",
|
||||
"download_iso_manual": "Wondering how to install it? Click ",
|
||||
"download_now": "Download Now",
|
||||
"download_mirrors_title": "Mirrors",
|
||||
"download_mirrors_description": "Maybe choosing a mirror site manually speeds up your download.",
|
||||
"download_mirrors_region": "Region",
|
||||
"download_mirrors_server": "Server",
|
||||
"download_mirrors_access": "Access",
|
||||
"download_container_title": "Running SemiOS in a Container",
|
||||
"download_container_direct_deploy": "Deploy Directly",
|
||||
"download_container_arch": "Architecture",
|
||||
"download_embedded_title": "Embedded Devices",
|
||||
"download_embedded_text": "To install SemiOS on an embedded device (e.g. demo boards), view our wiki to see supported devices and flashing guides.",
|
||||
"download_embedded_go": "Go to the Wiki Page"
|
||||
}
|
||||
|
||||
+30
-3
@@ -1,9 +1,16 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"nav_homepage": "ホームページ",
|
||||
"nav_download": "ダウンロード",
|
||||
"nav_packages": "パッケージ",
|
||||
"nav_wiki": "ウィキ",
|
||||
"foot_social": "ソーシャル",
|
||||
"homepage_slogan": "現代的で、高速かつ安全なオペレーティングシステム。",
|
||||
"homepage_slogan_detailed": "SemiOSは、高速性・安全性・柔軟性を追求して再設計されたシステムコンポーネントを備えた、汎用オペレーティングシステムです。\n\nデスクトップPCから組み込みボードまで、多様なプラットフォームで利用可能です。",
|
||||
"homepage_get": "SemiOS を入手",
|
||||
"packages_search_title": "パッケージを検索",
|
||||
"packages_mirror_select": "ミラーを選択",
|
||||
"packages_repo_select": "リポジトリを選択",
|
||||
"packages_search_keyword": "キーワード",
|
||||
"packages_arch_select": "アーキテクチャ",
|
||||
"packages_show_latest_only": "最新のみ表示",
|
||||
@@ -14,16 +21,36 @@
|
||||
"pkginfo_version": "バージョン",
|
||||
"pkginfo_arch": "アーキテクチャ",
|
||||
"pkginfo_installed_size": "インストールサイズ",
|
||||
"pkginfo_install_title": "インストール",
|
||||
"pkginfo_install_download": "ファイルをダウンロード",
|
||||
"pkginfo_install_packie_prompt_terminal": "このパッケージをPackieを使ってデフォルト環境にインストールするには、ターミナルで以下のコマンドを実行してください:",
|
||||
"pkginfo_install_packie_pasted": "コマンドを貼り付けました!",
|
||||
"pkginfo_download_size": "ダウンロードサイズ",
|
||||
"pkginfo_download_title": "パッケージをダウンロード",
|
||||
"pkginfo_download_action": "ダウンロード",
|
||||
"pkginfo_download_filename": "ダウンロードファイル名",
|
||||
"pkginfo_download_checksum": "チェックサム値",
|
||||
"pkginfo_dependency_title": "依存関係",
|
||||
"pkginfo_dependency_description": "これにより、このパッケージの依存関係と推奨パッケージが一覧表示されます。",
|
||||
"pkginfo_dependency_pkgspec": "パッケージ仕様",
|
||||
"pkginfo_dependency_type": "タイプ",
|
||||
"pkginfo_dependency_type_required": "必須",
|
||||
"pkginfo_dependency_type_recommended": "推奨",
|
||||
"homepage_slogan": "次世代コンピューティングのための革新的なオペレーティングシステム。",
|
||||
"homepage_get": "SemiOS を入手"
|
||||
"pkginfo_maintainers": "メンテナー",
|
||||
"download_iso_image": "ISO イメージを入手",
|
||||
"download_iso_image_description": "ワークステーションやサーバーなどの一般デバイス向けの最新 LiveCD イメージを選択して入手してください。",
|
||||
"download_iso_arch": "アーキテクチャ",
|
||||
"download_iso_image_ty": "イメージタイプ",
|
||||
"download_iso_manual": "インストール方法がわからない場合は、クリック ",
|
||||
"download_now": "今すぐ入手",
|
||||
"download_mirrors_title": "ミラー",
|
||||
"download_mirrors_description": "手動でミラーサイトを選択すると、ダウンロードが高速化される場合があります。",
|
||||
"download_mirrors_region": "リージョン",
|
||||
"download_mirrors_server": "サーバー",
|
||||
"download_mirrors_access": "アクセス",
|
||||
"download_container_title": "コンテナでSemiOSを実行する",
|
||||
"download_container_direct_deploy": "直接デプロイ",
|
||||
"download_container_arch": "アーキテクチャ",
|
||||
"download_embedded_title": "組み込みデバイス",
|
||||
"download_embedded_text": "組み込みデバイス(例:デモボード)にSemiOSをインストールするには、ウィキを参照して、サポートされているデバイスとフラッシュ書き込みガイドをご確認ください。",
|
||||
"download_embedded_go": "ウィキページへ進む"
|
||||
}
|
||||
|
||||
+31
-4
@@ -1,11 +1,18 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"nav_homepage": "首页",
|
||||
"nav_download": "下载",
|
||||
"nav_packages": "软件包",
|
||||
"nav_wiki": "文档",
|
||||
"foot_social": "社交媒体",
|
||||
"homepage_slogan": "现代、快速、安全的操作系统。",
|
||||
"homepage_slogan_detailed": "SemiOS 是一款通用操作系统,其系统组件经过重新设计,力求实现高速、安全与灵活。\n\n从桌面电脑到嵌入式开发板,它支持多种平台。",
|
||||
"homepage_get": "获取 SemiOS",
|
||||
"packages_search_title": "搜索软件包",
|
||||
"packages_mirror_select": "选择镜像站",
|
||||
"packages_repo_select": "选择仓库",
|
||||
"packages_search_keyword": "关键字",
|
||||
"packages_arch_select": "架构",
|
||||
"packages_arch_select": "筛选架构",
|
||||
"packages_show_latest_only": "仅显示最新版",
|
||||
"packages_search_action": "搜索",
|
||||
"packages_name": "软件包名称",
|
||||
@@ -14,16 +21,36 @@
|
||||
"pkginfo_version": "版本",
|
||||
"pkginfo_arch": "架构",
|
||||
"pkginfo_installed_size": "安装大小",
|
||||
"pkginfo_install_title": "安装软件包",
|
||||
"pkginfo_install_download": "下载文件",
|
||||
"pkginfo_install_packie_prompt_terminal": "要通过Packie在默认环境中安装此软件包,请在终端中执行以下命令:",
|
||||
"pkginfo_install_packie_pasted": "内容已复制!",
|
||||
"pkginfo_download_size": "下载大小",
|
||||
"pkginfo_download_title": "下载软件包",
|
||||
"pkginfo_download_action": "立即下载",
|
||||
"pkginfo_download_filename": "下载文件名",
|
||||
"pkginfo_download_checksum": "文件校验码",
|
||||
"pkginfo_dependency_title": "依赖关系",
|
||||
"pkginfo_dependency_description": "这里将会列举出此包依赖和建议安装的包。",
|
||||
"pkginfo_dependency_pkgspec": "软件包",
|
||||
"pkginfo_dependency_type": "类型",
|
||||
"pkginfo_dependency_type_required": "必需",
|
||||
"pkginfo_dependency_type_recommended": "建议",
|
||||
"homepage_slogan": "面向下一代计算的创新操作系统。",
|
||||
"homepage_get": "获取 SemiOS"
|
||||
"pkginfo_maintainers": "维护者",
|
||||
"download_iso_image": "获取 ISO 镜像",
|
||||
"download_iso_image_description": "选择并获取适用于工作站和服务器等通用设备的最新版本 LiveCD 镜像。",
|
||||
"download_iso_arch": "架构",
|
||||
"download_iso_image_ty": "镜像类型",
|
||||
"download_iso_manual": "想要获取安装指南?点击 ",
|
||||
"download_now": "立即下载",
|
||||
"download_mirrors_title": "镜像站",
|
||||
"download_mirrors_description": "有时手动选择一个镜像站可能提高下载软件的速度。",
|
||||
"download_mirrors_region": "地区",
|
||||
"download_mirrors_server": "服务器",
|
||||
"download_mirrors_access": "访问镜像站",
|
||||
"download_container_title": "在容器中运行 SemiOS",
|
||||
"download_container_direct_deploy": "直接部署",
|
||||
"download_container_arch": "架构",
|
||||
"download_embedded_title": "嵌入式设备",
|
||||
"download_embedded_text": "要在嵌入式设备(例如演示板)上安装SemiOS,请查看我们的维基页面,了解支持的设备和烧录指南。",
|
||||
"download_embedded_go": "前往维基页面"
|
||||
}
|
||||
|
||||
Generated
+35
-4
@@ -17,7 +17,7 @@
|
||||
"@fontsource-variable/inter": "^5.3.0",
|
||||
"@inlang/paraglide-js": "^2.18.2",
|
||||
"@internationalized/date": "^3.12.3",
|
||||
"@lucide/svelte": "^1.29.0",
|
||||
"@lucide/svelte": "^1.31.0",
|
||||
"@storybook/addon-a11y": "^10.5.7",
|
||||
"@storybook/addon-docs": "^10.5.7",
|
||||
"@storybook/addon-svelte-csf": "latest",
|
||||
@@ -49,6 +49,7 @@
|
||||
"storybook": "^10.5.7",
|
||||
"svelte": "^5.56.1",
|
||||
"svelte-check": "^4.6.0",
|
||||
"svelte-sonner": "^1.2.1",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
"tailwind-variants": "^3.3.1",
|
||||
"tailwindcss": "^4.3.0",
|
||||
@@ -1033,9 +1034,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@lucide/svelte": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.29.0.tgz",
|
||||
"integrity": "sha512-qzpfzxG8WZiAnyjh1qaQCtG4MA5R6HOyvb3c+LezexgFf69ptBdK3PjvGYGCWHKgTIVZMsOU9fqfczjsZAJl0A==",
|
||||
"version": "1.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.31.0.tgz",
|
||||
"integrity": "sha512-pgPuLEJdF0UpsajG9eSw0YvqXVuDxB+/x58BRiC4xE/KEHiopOqT1id3KCUvRN/PUmQkN1F26sslcs/1jutlBQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
@@ -6558,6 +6559,36 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-sonner": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.2.1.tgz",
|
||||
"integrity": "sha512-N00LOWb60fjKXULbpngku+7E9Krb16KpNq4/Eax5Vu7o1Dpbca55QpaLPsB+yp82JIEdFhfJuv5cVXLoe0ZtVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"runed": "^0.28.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-sonner/node_modules/runed": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/runed/-/runed-0.28.0.tgz",
|
||||
"integrity": "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/huntabyte",
|
||||
"https://github.com/sponsors/tglide"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esm-env": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-toolbelt": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.1.tgz",
|
||||
|
||||
+2
-1
@@ -21,7 +21,7 @@
|
||||
"@fontsource-variable/inter": "^5.3.0",
|
||||
"@inlang/paraglide-js": "^2.18.2",
|
||||
"@internationalized/date": "^3.12.3",
|
||||
"@lucide/svelte": "^1.29.0",
|
||||
"@lucide/svelte": "^1.31.0",
|
||||
"@storybook/addon-a11y": "^10.5.7",
|
||||
"@storybook/addon-docs": "^10.5.7",
|
||||
"@storybook/addon-svelte-csf": "latest",
|
||||
@@ -53,6 +53,7 @@
|
||||
"storybook": "^10.5.7",
|
||||
"svelte": "^5.56.1",
|
||||
"svelte-check": "^4.6.0",
|
||||
"svelte-sonner": "^1.2.1",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
"tailwind-variants": "^3.3.1",
|
||||
"tailwindcss": "^4.3.0",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/ui/button/button.svelte';
|
||||
import Separator from '$lib/components/ui/separator/separator.svelte';
|
||||
import { GitBranch } from '@lucide/svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
|
||||
import * as m from '$lib/paraglide/messages';
|
||||
</script>
|
||||
|
||||
<Separator class="mt-6" />
|
||||
|
||||
<div class="mt-4 mb-4 flex flex-col justify-between md:flex-row">
|
||||
<div
|
||||
class="flex flex-col items-center gap-1 text-center text-sm md:ml-6 md:items-start md:text-start"
|
||||
>
|
||||
<span>Copyright 2026 SemiOS Developers</span>
|
||||
<a href={resolve('/policy/privacy')} class="underline"> Privacy Policy </a>
|
||||
</div>
|
||||
<div class="flex items-center justify-center gap-2 md:mr-6">
|
||||
<span class="text-sm">{m.foot_social()}:</span>
|
||||
<a href="https://gitea.semilabs.org/semios">
|
||||
<Button size="icon" variant="ghost">
|
||||
<GitBranch />
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Label } from "$lib/components/ui/label/index.js";
|
||||
|
||||
import * as m from "$lib/paraglide/messages";
|
||||
import { resolve } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<div class="flex max-w-6xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
|
||||
@@ -16,10 +17,22 @@
|
||||
</NavigationMenu.Item>
|
||||
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Link href="/packages.html" class="text-sm font-medium">
|
||||
<NavigationMenu.Link href={resolve("/packages")} class="text-sm font-medium">
|
||||
{m.nav_packages()}
|
||||
</NavigationMenu.Link>
|
||||
</NavigationMenu.Item>
|
||||
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Link href="/download" class="text-sm font-medium">
|
||||
{m.nav_download()}
|
||||
</NavigationMenu.Link>
|
||||
</NavigationMenu.Item>
|
||||
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Link href="https://wiki.os.semilabs.org" class="text-sm font-medium">
|
||||
{m.nav_wiki()}
|
||||
</NavigationMenu.Link>
|
||||
</NavigationMenu.Item>
|
||||
</NavigationMenu.List>
|
||||
</NavigationMenu.Root>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
|
||||
</script>
|
||||
|
||||
<span
|
||||
bind:this={ref}
|
||||
data-slot="avatar-badge"
|
||||
class={cn(
|
||||
"bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none",
|
||||
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
||||
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
||||
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</span>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AvatarPrimitive.FallbackProps = $props();
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Fallback
|
||||
bind:ref
|
||||
data-slot="avatar-fallback"
|
||||
class={cn(
|
||||
"rounded-full bg-muted text-muted-foreground flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="avatar-group-count"
|
||||
class={cn(
|
||||
"size-8 rounded-full bg-muted text-sm text-muted-foreground group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 relative flex shrink-0 items-center justify-center ring-2 ring-background",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="avatar-group"
|
||||
class={cn(
|
||||
"cn-avatar-group group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AvatarPrimitive.ImageProps = $props();
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Image
|
||||
bind:ref
|
||||
data-slot="avatar-image"
|
||||
class={cn("rounded-full aspect-square size-full object-cover", className)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
loadingStatus = $bindable("loading"),
|
||||
size = "default",
|
||||
class: className,
|
||||
...restProps
|
||||
}: AvatarPrimitive.RootProps & {
|
||||
size?: "default" | "sm" | "lg";
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Root
|
||||
bind:ref
|
||||
bind:loadingStatus
|
||||
data-slot="avatar"
|
||||
data-size={size}
|
||||
class={cn(
|
||||
"size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:border-border after:mix-blend-darken dark:after:mix-blend-lighten",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,22 @@
|
||||
import Badge from "./avatar-badge.svelte";
|
||||
import Fallback from "./avatar-fallback.svelte";
|
||||
import GroupCount from "./avatar-group-count.svelte";
|
||||
import Group from "./avatar-group.svelte";
|
||||
import Image from "./avatar-image.svelte";
|
||||
import Root from "./avatar.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
Image,
|
||||
Fallback,
|
||||
Badge,
|
||||
Group,
|
||||
GroupCount,
|
||||
//
|
||||
Root as Avatar,
|
||||
Image as AvatarImage,
|
||||
Fallback as AvatarFallback,
|
||||
Badge as AvatarBadge,
|
||||
Group as AvatarGroup,
|
||||
GroupCount as AvatarGroupCount,
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Toaster } from "./sonner.svelte";
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { mode } from "mode-watcher";
|
||||
import { Toaster as Sonner, type ToasterProps as SonnerProps } from "svelte-sonner";
|
||||
import Loader2Icon from '@lucide/svelte/icons/loader-2';
|
||||
import CircleCheckIcon from '@lucide/svelte/icons/circle-check';
|
||||
import OctagonXIcon from '@lucide/svelte/icons/octagon-x';
|
||||
import InfoIcon from '@lucide/svelte/icons/info';
|
||||
import TriangleAlertIcon from '@lucide/svelte/icons/triangle-alert';
|
||||
|
||||
let { ...restProps }: SonnerProps = $props();
|
||||
</script>
|
||||
|
||||
<Sonner
|
||||
theme={mode.current}
|
||||
class="toaster group"
|
||||
style="--normal-bg: var(--color-popover); --normal-text: var(--color-popover-foreground); --normal-border: var(--color-border);"
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet loadingIcon()}
|
||||
<Loader2Icon class="size-4 animate-spin" />
|
||||
{/snippet}
|
||||
{#snippet successIcon()}
|
||||
<CircleCheckIcon class="size-4" />
|
||||
{/snippet}
|
||||
{#snippet errorIcon()}
|
||||
<OctagonXIcon class="size-4" />
|
||||
{/snippet}
|
||||
{#snippet infoIcon()}
|
||||
<InfoIcon class="size-4" />
|
||||
{/snippet}
|
||||
{#snippet warningIcon()}
|
||||
<TriangleAlertIcon class="size-4" />
|
||||
{/snippet}
|
||||
</Sonner>
|
||||
@@ -0,0 +1,18 @@
|
||||
import Content from "./tabs-content.svelte";
|
||||
import Trigger from "./tabs-trigger.svelte";
|
||||
import Root from "./tabs.svelte";
|
||||
import List, { tabsListVariants, type TabsListVariant } from "./tabs-list.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
List,
|
||||
Trigger,
|
||||
tabsListVariants,
|
||||
type TabsListVariant,
|
||||
//
|
||||
Root as Tabs,
|
||||
Content as TabsContent,
|
||||
List as TabsList,
|
||||
Trigger as TabsTrigger,
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: TabsPrimitive.ContentProps = $props();
|
||||
</script>
|
||||
|
||||
<TabsPrimitive.Content
|
||||
bind:ref
|
||||
data-slot="tabs-content"
|
||||
class={cn("text-sm flex-1 outline-none", className)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,40 @@
|
||||
<script lang="ts" module>
|
||||
import { tv, type VariantProps } from "tailwind-variants";
|
||||
|
||||
export const tabsListVariants = tv({
|
||||
base: "rounded-full p-1 group-data-horizontal/tabs:h-9 group-data-vertical/tabs:rounded-2xl data-[variant=line]:rounded-none group/tabs-list inline-flex w-fit items-center justify-center text-muted-foreground group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
|
||||
variants: {
|
||||
variant: {
|
||||
default: "cn-tabs-list-variant-default bg-muted",
|
||||
line: "cn-tabs-list-variant-line gap-1 bg-transparent",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
});
|
||||
|
||||
export type TabsListVariant = VariantProps<typeof tabsListVariants>["variant"];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
variant = "default",
|
||||
class: className,
|
||||
...restProps
|
||||
}: TabsPrimitive.ListProps & {
|
||||
variant?: TabsListVariant;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<TabsPrimitive.List
|
||||
bind:ref
|
||||
data-slot="tabs-list"
|
||||
data-variant={variant}
|
||||
class={cn(tabsListVariants({ variant }), className)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: TabsPrimitive.TriggerProps = $props();
|
||||
</script>
|
||||
|
||||
<TabsPrimitive.Trigger
|
||||
bind:ref
|
||||
data-slot="tabs-trigger"
|
||||
class={cn(
|
||||
"gap-2 rounded-full border border-transparent! px-3 py-1 text-sm font-medium group-data-vertical/tabs:rounded-2xl group-data-vertical/tabs:px-3 group-data-vertical/tabs:py-1.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4 relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
||||
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
||||
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
|
||||
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(""),
|
||||
class: className,
|
||||
...restProps
|
||||
}: TabsPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<TabsPrimitive.Root
|
||||
bind:ref
|
||||
bind:value
|
||||
data-slot="tabs"
|
||||
class={cn("gap-2 group/tabs flex data-[orientation=horizontal]:flex-col", className)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,15 @@
|
||||
export const MIRRORS: Record<string, string[]> = {
|
||||
"Global": [
|
||||
"mirror1.semilabs.org",
|
||||
],
|
||||
"East Asia": [
|
||||
"mirror2.semilabs.org",
|
||||
],
|
||||
};
|
||||
|
||||
export const MAINTAINERS: Record<string, { name: string, avatar: string }> = {
|
||||
"sisungo <[email protected]>": {
|
||||
"name": "sisungo",
|
||||
"avatar": "https://github.com/sisungo.png",
|
||||
},
|
||||
}
|
||||
@@ -6,12 +6,15 @@
|
||||
import './layout.css';
|
||||
import { ModeWatcher } from 'mode-watcher';
|
||||
import favicon from '$lib/assets/favicon.svg';
|
||||
import Topbar from '../components/Topbar.svelte';
|
||||
import { setLocale } from "$lib/paraglide/runtime";
|
||||
import { Toaster } from "$lib/components/ui/sonner/index.js";
|
||||
|
||||
import Topbar from '../components/Topbar.svelte';
|
||||
import Pagefoot from '../components/Pagefoot.svelte';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
function normalizeLanguage(lang) {
|
||||
function normalizeLanguage(lang: string): 'zh-cn' | 'ja' | 'en' | null {
|
||||
if (!lang) return null;
|
||||
|
||||
lang = lang.toLowerCase();
|
||||
@@ -28,8 +31,10 @@
|
||||
|
||||
<svelte:head><link rel="icon" href={favicon} /></svelte:head>
|
||||
<ModeWatcher />
|
||||
<Toaster />
|
||||
<Topbar />
|
||||
{@render children()}
|
||||
<Pagefoot />
|
||||
|
||||
<div style="display:none">
|
||||
{#each locales as locale (locale)}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import * as m from "$lib/paraglide/messages";
|
||||
import { resolve } from "$app/paths";
|
||||
|
||||
import Button from "$lib/components/ui/button/button.svelte";
|
||||
</script>
|
||||
@@ -9,9 +10,12 @@
|
||||
<h1 class="text-4xl font-bold">
|
||||
{m.homepage_slogan()}
|
||||
</h1>
|
||||
<Button class="mt-4 flex">
|
||||
<p class="mt-2 text-muted-foreground whitespace-pre-line">{m.homepage_slogan_detailed()}</p>
|
||||
<a href={resolve("/download")}>
|
||||
<Button class="mt-6 flex">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||
<p>{m.homepage_get()}</p>
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,174 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import Label from '$lib/components/ui/label/label.svelte';
|
||||
import * as Select from '$lib/components/ui/select/index.js';
|
||||
import * as Tabs from '$lib/components/ui/tabs/index.js';
|
||||
|
||||
import { Archive, CircleArrowRight, Container, Cpu, Disc, Download } from '@lucide/svelte';
|
||||
|
||||
import * as m from '$lib/paraglide/messages';
|
||||
import { MIRRORS } from '$lib/mirrors';
|
||||
|
||||
let isoImageArch = $state('x86_64');
|
||||
let isoImageTy = $state('gui');
|
||||
|
||||
let mirrorRegion = $state('Global');
|
||||
let mirrorServer = $state('mirror1.semilabs.org');
|
||||
|
||||
let containerArch = $state('x86_64-semios-linux');
|
||||
</script>
|
||||
|
||||
<div class="mr-4 ml-4">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title class="flex items-center gap-2">
|
||||
<Disc />
|
||||
{m.download_iso_image()}
|
||||
</Card.Title>
|
||||
<Card.Description>{m.download_iso_image_description()}</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="flex gap-4 flex-col md:flex-row">
|
||||
<div>
|
||||
<Label class="mb-2 text-sm">{m.download_iso_arch()}</Label>
|
||||
<Select.Root type="single" bind:value={isoImageArch}>
|
||||
<Select.Trigger class="w-[180px]">{isoImageArch}</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="x86_64">x86_64</Select.Item>
|
||||
<Select.Item value="aarch64">aarch64</Select.Item>
|
||||
<Select.Item value="riscv64">riscv64</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="mb-2 text-sm">{m.download_iso_image_ty()}</Label>
|
||||
<Select.Root type="single" bind:value={isoImageTy}>
|
||||
<Select.Trigger class="w-[180px]">{isoImageTy}</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="netboot">NetBoot</Select.Item>
|
||||
<Select.Item value="minimal">Minimal LiveCD</Select.Item>
|
||||
<Select.Item value="gui">GUI LiveCD</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
{m.download_iso_manual()}
|
||||
<a href="https://wiki.os.semilabs.org/Installation_Guide" class="underline">Installation Guide</a>
|
||||
</div>
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<Button variant="default">
|
||||
<Download />
|
||||
{m.download_now()}
|
||||
</Button>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
|
||||
<div class="mt-4 flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-4 md:flex-row">
|
||||
<Card.Root class="w-full">
|
||||
<Card.Header>
|
||||
<Card.Title class="flex items-center gap-2">
|
||||
<Container />
|
||||
{m.download_container_title()}
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<Tabs.Root value="direct_deploy" class="w-[400px]">
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger value="direct_deploy"
|
||||
>{m.download_container_direct_deploy()}</Tabs.Trigger
|
||||
>
|
||||
<Tabs.Trigger value="docker">Docker</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Tabs.Content value="direct_deploy">
|
||||
<div>
|
||||
<Label class="mb-2 text-sm">{m.download_container_arch()}</Label>
|
||||
<Select.Root type="single" bind:value={containerArch}>
|
||||
<Select.Trigger class="w-[180px]">{containerArch}</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="x86_64-semios-linux">x86_64-semios-linux</Select.Item>
|
||||
<Select.Item value="aarch64-semios-linux">aarch64-semios-linux</Select.Item>
|
||||
<Select.Item value="riscv64-semios-linux">riscv64-semios-linux</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<Button class="mt-4">
|
||||
<Download />
|
||||
{m.download_now()}
|
||||
</Button>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="docker">Not yet available.</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<Card.Root class="w-full">
|
||||
<Card.Header>
|
||||
<Card.Title class="flex items-center gap-2">
|
||||
<Archive />
|
||||
{m.download_mirrors_title()}
|
||||
</Card.Title>
|
||||
<Card.Description>{m.download_mirrors_description()}</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="flex flex-col gap-2 md:flex-row">
|
||||
<div>
|
||||
<Label class="mb-2 text-sm">{m.download_mirrors_region()}</Label>
|
||||
<Select.Root type="single" bind:value={mirrorRegion}>
|
||||
<Select.Trigger class="w-[180px]">{mirrorRegion}</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each Object.keys(MIRRORS) as region (region)}
|
||||
<Select.Item
|
||||
value={region}
|
||||
onclick={() => {
|
||||
mirrorServer = MIRRORS[region][0];
|
||||
}}>{region}</Select.Item
|
||||
>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="mb-2 text-sm">{m.download_mirrors_server()}</Label>
|
||||
<Select.Root type="single" bind:value={mirrorServer}>
|
||||
<Select.Trigger class="w-[180px]">{mirrorServer}</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each MIRRORS[mirrorRegion] as server (server)}
|
||||
<Select.Item value={server}>{server}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<a href={`https://${mirrorServer}/semios/images`}>
|
||||
<Button>
|
||||
<CircleArrowRight />
|
||||
{m.download_mirrors_access()}
|
||||
</Button>
|
||||
</a>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4 md:flex-row">
|
||||
<Card.Root class="w-full">
|
||||
<Card.Header>
|
||||
<Card.Title class="flex items-center gap-2">
|
||||
<Cpu /> {m.download_embedded_title()}
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<p>{m.download_embedded_text()}</p>
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<a href="http://wiki.os.semilabs.org/Embedded_Devices" class="underline">{m.download_embedded_go()}</a>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,20 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import * as Card from "$lib/components/ui/card/index.js";
|
||||
import Checkbox from "$lib/components/ui/checkbox/checkbox.svelte";
|
||||
import { Input } from "$lib/components/ui/input/index.js";
|
||||
import * as Select from "$lib/components/ui/select/index.js";
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
|
||||
import { Input } from '$lib/components/ui/input/index.js';
|
||||
import * as Select from '$lib/components/ui/select/index.js';
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
|
||||
import { SvelteMap } from "svelte/reactivity";
|
||||
import * as m from "$lib/paraglide/messages";
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import * as m from '$lib/paraglide/messages';
|
||||
import { resolve } from '$app/paths';
|
||||
|
||||
import { getRepoDb, searchPackages, type PkgIdent, baseUrlForMirror, strPkgIdent } from "./helper.ts";
|
||||
import {
|
||||
getRepoDb,
|
||||
searchPackages,
|
||||
type PkgIdent,
|
||||
baseUrlForMirror,
|
||||
strPkgIdent
|
||||
} from './helper.ts';
|
||||
import Label from '$lib/components/ui/label/label.svelte';
|
||||
import { Axe, Server } from '@lucide/svelte';
|
||||
|
||||
let showLatestOnly = $state(false);
|
||||
let filterArchitecture = $state("");
|
||||
let selectedMirror = $state("mirror1.semilabs.org");
|
||||
let searchKeyword = $state("");
|
||||
let filterArchitecture = $state('');
|
||||
let selectedMirror = $state('mirror1.semilabs.org');
|
||||
let selectedRepo = $state('official');
|
||||
let searchKeyword = $state('');
|
||||
let results = $state<Array<PkgIdent>>([]);
|
||||
|
||||
async function search() {
|
||||
@@ -22,7 +32,7 @@
|
||||
const repo = await getRepoDb(`${baseUrl}/repo.db`);
|
||||
results = searchPackages(repo, searchKeyword);
|
||||
if (filterArchitecture) {
|
||||
results = results.filter((item) => item.arch === filterArchitecture || item.arch === "any");
|
||||
results = results.filter((item) => item.arch === filterArchitecture || item.arch === 'any');
|
||||
}
|
||||
if (showLatestOnly) {
|
||||
const latestResults = new SvelteMap<string, PkgIdent>();
|
||||
@@ -35,30 +45,69 @@
|
||||
results = Array.from(latestResults.values());
|
||||
}
|
||||
}
|
||||
|
||||
function pkgInfoUrl(mirrorName: string, pkgIdent: PkgIdent): string {
|
||||
const params = {
|
||||
mirrorName,
|
||||
pkgIdent: strPkgIdent(pkgIdent),
|
||||
};
|
||||
const searchParams = new URLSearchParams(params);
|
||||
return resolve(`/packages/pkginfo?${searchParams}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ml-4 mr-4">
|
||||
<Card.Root>
|
||||
<div class="mr-4 ml-4">
|
||||
<div class="flex">
|
||||
<Card.Root class="w-full">
|
||||
<Card.Header>
|
||||
<Card.Title>{m.packages_search_title()}</Card.Title>
|
||||
<Card.Title class="flex w-full items-center justify-between">
|
||||
<Label class="text-xl font-bold">{m.packages_search_title()}</Label>
|
||||
<div class="flex gap-2">
|
||||
<Button variant="outline" size="icon" class="float-end">
|
||||
<Server />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" class="float-end">
|
||||
<Axe />
|
||||
</Button>
|
||||
</div>
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-col gap-4">
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div>
|
||||
<Label class="text-sm">{m.packages_mirror_select()}</Label>
|
||||
<Select.Root type="single" bind:value={selectedMirror}>
|
||||
<Select.Trigger class="w-[180px]">{m.packages_mirror_select()}</Select.Trigger>
|
||||
<Select.Trigger class="mt-1 w-[180px]">{selectedMirror}</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="mirror1.semilabs.org">mirror1.semilabs.org</Select.Item>
|
||||
<Select.Item value="mirror2.semilabs.org">mirror2.semilabs.org</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
<Input placeholder={m.packages_search_keyword()} bind:value={searchKeyword} class="w-1/2" />
|
||||
<div class="flex items-center gap-4">
|
||||
<Select.Root type="single" bind:value={filterArchitecture}>
|
||||
<Select.Trigger class="w-[180px]">{m.packages_arch_select()}</Select.Trigger>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="text-sm">{m.packages_repo_select()}</Label>
|
||||
<Select.Root type="single" bind:value={selectedRepo}>
|
||||
<Select.Trigger class="mt-1 w-[180px]">{selectedRepo}</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="official">official</Select.Item>
|
||||
<Select.Item value="unofficial">unofficial</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="text-sm">{m.packages_arch_select()}</Label>
|
||||
<Select.Root type="single" bind:value={filterArchitecture}>
|
||||
<Select.Trigger class="mt-1 w-[180px]">{filterArchitecture}</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="">none</Select.Item>
|
||||
<Select.Item value="aarch64-semios-linux">aarch64-semios-linux</Select.Item>
|
||||
<Select.Item value="x86_64-semios-linux">x86_64-semios-linux</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
<Input placeholder={m.packages_search_keyword()} bind:value={searchKeyword} class="mt-4 md:mt-0 md:w-1/2" />
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox bind:checked={showLatestOnly} />
|
||||
<span>{m.packages_show_latest_only()}</span>
|
||||
@@ -71,7 +120,7 @@
|
||||
</Button>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-4 overflow-x-auto">
|
||||
<Table.Root>
|
||||
@@ -83,9 +132,13 @@
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each results as item}
|
||||
{#each results as item (strPkgIdent(item))}
|
||||
<Table.Row>
|
||||
<Table.Cell><a href={`/packages/pkginfo?pkgIdent=${strPkgIdent(item)}&mirrorName=${selectedMirror}`}>{item.name}</a></Table.Cell>
|
||||
<Table.Cell>
|
||||
<a href={pkgInfoUrl(selectedMirror, item)}>
|
||||
{item.name}
|
||||
</a>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{item.version}</Table.Cell>
|
||||
<Table.Cell>{item.arch}</Table.Cell>
|
||||
</Table.Row>
|
||||
|
||||
@@ -41,7 +41,9 @@ export function baseUrlForMirror(mirror: string): string {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export async function getRepoDb(url: string): Promise<any> {
|
||||
const sqlPromise = initSqlJs();
|
||||
const sqlPromise = initSqlJs({
|
||||
locateFile: () => "/sql-wasm-browser.wasm",
|
||||
});
|
||||
const dataPromise = fetch(url).then((res) => res.arrayBuffer());
|
||||
const [SQL, data] = await Promise.all([sqlPromise, dataPromise]);
|
||||
return new SQL.Database(new Uint8Array(data));
|
||||
@@ -64,14 +66,15 @@ export function searchPackages(db: any, keyword: string): PkgIdent[] {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function getPkgInfo(db: any, pkg: PkgIdent): any {
|
||||
const stmt = db.prepare(
|
||||
`SELECT pkg_manifest, download_size, installed_size FROM package WHERE pkg_name = ? AND pkg_version = ? AND pkg_arch = ?`
|
||||
`SELECT pkg_manifest, download_size, installed_size, checksum FROM package WHERE pkg_name = ? AND pkg_version = ? AND pkg_arch = ?`
|
||||
);
|
||||
stmt.bind([pkg.name, pkg.version, pkg.arch]);
|
||||
if (stmt.step()) {
|
||||
const [sManifest, downloadSize, installedSize] = stmt.get();
|
||||
const [sManifest, downloadSize, installedSize, checksum] = stmt.get();
|
||||
const manifest = JSON.parse(sManifest);
|
||||
manifest['download_size'] = downloadSize;
|
||||
manifest['installed_size'] = installedSize;
|
||||
manifest['checksum'] = checksum
|
||||
return manifest;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -6,13 +6,27 @@
|
||||
import { page } from '$app/state';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { onMount } from 'svelte';
|
||||
import * as m from "$lib/paraglide/messages";
|
||||
import * as m from '$lib/paraglide/messages';
|
||||
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import * as Card from "$lib/components/ui/card/index.js";
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
import * as Avatar from '$lib/components/ui/avatar/index.js';
|
||||
import * as Tabs from '$lib/components/ui/tabs/index.js';
|
||||
import * as Item from '$lib/components/ui/item/index.js';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import { getRepoDb, parsePkgIdent, baseUrlForMirror, getPkgInfo, formatSize, type PkgIdent, strPkgIdent } from '../helper.ts';
|
||||
import {
|
||||
getRepoDb,
|
||||
parsePkgIdent,
|
||||
baseUrlForMirror,
|
||||
getPkgInfo,
|
||||
formatSize,
|
||||
type PkgIdent,
|
||||
strPkgIdent
|
||||
} from '../helper.ts';
|
||||
import { ArrowDownToLine, ClipboardCopy, Package, Ungroup, UsersRound } from '@lucide/svelte';
|
||||
import Label from '$lib/components/ui/label/label.svelte';
|
||||
|
||||
let baseUrl: string | null = $state(null);
|
||||
let pkgInfo = $state(null);
|
||||
@@ -41,48 +55,106 @@
|
||||
if (!pkgInfo) {
|
||||
error(404);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="ml-4 mr-4 flex flex-col md:flex-row gap-4">
|
||||
<div class="md:w-1/2 flex flex-col gap-4">
|
||||
<div class="mr-4 ml-4 flex flex-col gap-4 md:flex-row">
|
||||
<div class="flex flex-col gap-4 md:w-1/2">
|
||||
<Card.Root class="h-fit">
|
||||
<Card.Header>
|
||||
<Card.Title class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-package-icon lucide-package"><path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"/><path d="M12 22V12"/><polyline points="3.29 7 12 12 20.71 7"/><path d="m7.5 4.27 9 5.15"/></svg>
|
||||
{pkgInfo ? pkgInfo['name'] : ""}
|
||||
<Package />
|
||||
{pkgInfo ? pkgInfo['name'] : ''}
|
||||
</Card.Title>
|
||||
<Card.Description>{pkgInfo ? pkgInfo['description'] : null}</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<p>{m.pkginfo_version()}: {pkgInfo ? pkgInfo['version'] : ""}</p>
|
||||
<p>{m.pkginfo_arch()}: {pkgInfo ? pkgInfo['arch'] : ""}</p>
|
||||
<p>{m.pkginfo_installed_size()}: {pkgInfo ? formatSize(pkgInfo['installed_size']) : ""}</p>
|
||||
<div class="flex">
|
||||
<Label class="font-bold">{m.pkginfo_version()}: </Label>
|
||||
<span>{pkgInfo ? pkgInfo['version'] : ''}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<Label class="font-bold">{m.pkginfo_arch()}: </Label>
|
||||
<span>{pkgInfo ? pkgInfo['arch'] : ''}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<Label class="font-bold">{m.pkginfo_installed_size()}: </Label>
|
||||
<span>{pkgInfo ? formatSize(pkgInfo['installed_size']) : ''}</span>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root class="h-fit">
|
||||
<Card.Header>
|
||||
<Card.Title class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-to-line-icon lucide-arrow-down-to-line"><path d="M12 17V3"/><path d="m6 11 6 6 6-6"/><path d="M19 21H5"/></svg>
|
||||
{m.pkginfo_download_title()}
|
||||
<ArrowDownToLine />
|
||||
{m.pkginfo_install_title()}
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<p>{m.pkginfo_download_filename()}: {(pkgIdent ? strPkgIdent(pkgIdent) : "") + ".pkg"}</p>
|
||||
<p>{m.pkginfo_download_size()}: {pkgInfo ? formatSize(pkgInfo['download_size']) : ""}</p>
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<Button onclick={() => {
|
||||
<Tabs.Root value="file">
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger value="file">{m.pkginfo_install_download()}</Tabs.Trigger>
|
||||
<Tabs.Trigger value="packie">Packie</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Tabs.Content value="file" class="mt-2">
|
||||
<div class="flex min-w-0 items-center">
|
||||
<Label class="shrink-0 font-bold">{m.pkginfo_download_filename()}: </Label>
|
||||
<span class="truncate font-mono text-sm">
|
||||
{(pkgIdent ? strPkgIdent(pkgIdent) : '') + '.pkg'}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<Label class="font-bold">{m.pkginfo_download_size()}: </Label>
|
||||
<span>{pkgInfo ? formatSize(pkgInfo['download_size']) : ''}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<Label class="shrink-0 font-bold">{m.pkginfo_download_checksum()}: </Label>
|
||||
<code class="block overflow-x-auto bg-gray-100 whitespace-nowrap dark:bg-gray-800">
|
||||
{pkgInfo ? pkgInfo['checksum'] : ''}
|
||||
</code>
|
||||
</div>
|
||||
<Button
|
||||
class="mt-3"
|
||||
onclick={() => {
|
||||
window.location.href = `${baseUrl}/${(pkgIdent ? strPkgIdent(pkgIdent) : '') + '.pkg'}`;
|
||||
}}>{m.pkginfo_download_action()}</Button>
|
||||
</Card.Footer>
|
||||
}}>{m.pkginfo_download_action()}</Button
|
||||
>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="packie" class="mt-2">
|
||||
<p>{m.pkginfo_install_packie_prompt_terminal()}</p>
|
||||
<Item.Root variant="muted" class="flex items-center gap-2 mt-2">
|
||||
<Item.Content class="min-w-0 flex-1">
|
||||
<div class="flex items-center overflow-x-auto whitespace-nowrap">
|
||||
<Label class="shrink-0">$ </Label>
|
||||
<code class="font-mono text-sm">
|
||||
packie install '{pkgIdent ? strPkgIdent(pkgIdent) : ''}'
|
||||
</code>
|
||||
</div>
|
||||
</Item.Content>
|
||||
<Item.Actions class="shrink-0">
|
||||
<Button
|
||||
variant="default"
|
||||
size="icon"
|
||||
onclick={() => {
|
||||
const command = `packie install '${pkgIdent ? strPkgIdent(pkgIdent) : ''}'`;
|
||||
navigator.clipboard.writeText(command);
|
||||
toast(m.pkginfo_install_packie_pasted(), { description: command });
|
||||
}}
|
||||
>
|
||||
<ClipboardCopy />
|
||||
</Button>
|
||||
</Item.Actions>
|
||||
</Item.Root>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
<div class="md:w-1/2">
|
||||
<div class="flex flex-col gap-4 md:w-1/2">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ungroup-icon lucide-ungroup"><rect x="11" y="14" width="10" height="7" rx="2"/><rect x="3" y="3" width="10" height="7" rx="2"/></svg>
|
||||
<Ungroup />
|
||||
{m.pkginfo_dependency_title()}
|
||||
</Card.Title>
|
||||
<Card.Description>{m.pkginfo_dependency_description()}</Card.Description>
|
||||
@@ -96,13 +168,13 @@
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each pkgInfo ? pkgInfo['dependencies'] : [] as item}
|
||||
{#each pkgInfo ? pkgInfo['dependencies'] : [] as item (item)}
|
||||
<Table.Row>
|
||||
<Table.Cell>{item}</Table.Cell>
|
||||
<Table.Cell>{m.pkginfo_dependency_type_required()}</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
{#each pkgInfo ? pkgInfo['recommendations'] : [] as item}
|
||||
{#each pkgInfo ? pkgInfo['recommendations'] : [] as item (item)}
|
||||
<Table.Row>
|
||||
<Table.Cell>{item}</Table.Cell>
|
||||
<Table.Cell>{m.pkginfo_dependency_type_recommended()}</Table.Cell>
|
||||
@@ -112,5 +184,23 @@
|
||||
</Table.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title class="flex items-center gap-2">
|
||||
<UsersRound />
|
||||
{m.pkginfo_maintainers()}
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
{#each pkgInfo ? pkgInfo['maintainers'] : [] as item (item)}
|
||||
<div>
|
||||
<Avatar.Root>
|
||||
<Avatar.Image />
|
||||
<Avatar.Fallback>User</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
</div>
|
||||
{/each}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<script>
|
||||
import Separator from "$lib/components/ui/separator/separator.svelte";
|
||||
|
||||
</script>
|
||||
<div class="ml-6 mr-6 mt-6">
|
||||
<h1 class="text-3xl font-bold">Privacy Policy</h1>
|
||||
<p class="text-muted-foreground text-sm mt-1">Updated: 2026-08-18</p>
|
||||
<Separator class="mt-2 mb-2" />
|
||||
This privacy policy describes how we use your personal data collected when you access our website.
|
||||
</div>
|
||||
Reference in New Issue
Block a user