This commit is contained in:
despiegk 2025-05-11 12:55:47 +03:00
parent 19ae834c99
commit 373820ec8a
31 changed files with 1132 additions and 3 deletions

View File

@ -17,10 +17,10 @@ fi
# Install Svelte with Bun and Vite
echo "Installing Svelte with Bun and Vite..."
bun create vite my-svelte-app --template svelte-ts
bun create vite sweb --template svelte-ts
# Change into the new app directory
cd my-svelte-app
cd sweb
# Install dependencies
bun install
@ -61,4 +61,8 @@ EOF
# Install Shadcn for Svelte
echo "Installing Shadcn for Svelte..."
bunx shadcn-svelte init --style default --base-color slate --css src/app.css --tailwind-config tailwind.config.js
bun install lucide-svelte
# Attempting to run Shadcn Svelte init non-interactively by providing the components alias.
bunx shadcn-svelte init --style default --base-color slate --css src/app.css --tailwind-config tailwind.config.js --components-alias '$lib/components/ui'

49
specs/architecture.md Normal file
View File

@ -0,0 +1,49 @@
# Secure Browser-Hosted Website Architecture
This document outlines the architecture for a secure website hosted entirely within the user's browser, with all content and metadata retrieved from IPFS.
## Core Principles
- **Decentralization:** All website assets (metadata, markdown pages, images) are stored on IPFS, removing the need for a centralized web server for content delivery.
- **Browser-Based Execution:** The website logic runs directly in the user's browser using standard web technologies (HTML, CSS, JavaScript).
- **IPFS Client:** A client-side IPFS library is used to fetch content based on content identifiers (CIDs/hashes).
- **Metadata-Driven:** The website structure, navigation, and content references are defined by a set of JSON metadata files.
- **Security:** The architecture prioritizes security by leveraging the immutability and content-addressing properties of IPFS.
## Components
1. **Browser Environment:** The user's web browser, responsible for executing the website's code and rendering the content.
2. **Website Code:** HTML, CSS, and JavaScript files that constitute the website's front-end logic. These files are initially loaded into the browser (potentially also from IPFS, though the initial load mechanism is outside the scope of this document).
3. **IPFS Client Library:** A JavaScript library running in the browser that interacts with the IPFS network to fetch data.
4. **Metadata Files:** JSON files stored on IPFS that define:
- Header structure
- Footer structure
- Navbar structure
- List of all available pages, including their IPFS hashes.
5. **Content Files:** Markdown files and image files stored on IPFS, organized into collections.
## Workflow
1. The user's browser loads the initial website code.
2. The website code uses the IPFS client library to fetch the main metadata file (which itself could be referenced by a known IPFS hash or other mechanism).
3. The main metadata file contains references (IPFS hashes) to the header, footer, navbar, and pages list metadata files.
4. The website code fetches these individual metadata files using the IPFS client.
5. Based on the navbar metadata and pages list, the website renders the navigation.
6. When a user navigates to a specific page:
- The website code identifies the corresponding entry in the pages list metadata.
- It retrieves the markdown content for the page from IPFS using the associated hash.
- It parses and renders the markdown content.
- Any referenced images within the markdown (which are part of the same collection) are also fetched from IPFS using their respective hashes.
## Advantages
- **Security:** Content immutability on IPFS prevents tampering after publication.
- **Resilience:** Content can be served from any IPFS node that has it, reducing reliance on a single server.
- **Efficiency:** Content addressing means fetching is based on content itself, not location.
## Considerations
- Initial loading mechanism of the website code.
- Handling of IPFS network connectivity and potential errors.
- Caching strategies for metadata and content.
- Search and indexing of content (requires external services or pre-computation).

View File

@ -0,0 +1,41 @@
# Content Processing Pipeline
This document describes the pipeline for processing raw content files (markdown and images) from collection directories before they are made available on IPFS and referenced in the website's metadata. This process ensures content integrity, enables encryption, and prepares files for decentralized storage.
## Pipeline Steps
The content processing pipeline involves the following steps for each file found within a collection directory (identified by the presence of a `.collection` file):
1. **File Discovery:** Identify all files directly within a collection directory. Subdirectories and their contents are ignored.
2. **Filename Normalization:** The filename is converted to lowercase and snake_case. This normalized filename is used for addressing the content within the website (as part of the page path).
3. **Original Content Hashing (Blake):** The original, unencrypted content of the file is hashed using the Blake algorithm. This Blake hash serves two purposes:
- It acts as the symmetric encryption key for the file content.
- It is stored in the metadata as part of the key to retrieve and decrypt the content.
4. **Content Encryption:** The original file content is encrypted using a very strong symmetric encryption scheme. The Blake hash calculated in the previous step is used as the encryption key. The specific encryption algorithm and implementation details should be chosen to ensure high security.
5. **Encrypted Content Upload to IPFS:** The resulting encrypted content is uploaded to the IPFS network. The IPFS network processes the encrypted content and returns a Content Identifier (CID), which is a hash of the *encrypted* content. This CID is the address on IPFS where the encrypted file can be retrieved.
6. **Metadata Key Generation:** A key is generated for the metadata by concatenating the Blake hash (the encryption key) and the IPFS hash (CID) of the encrypted content, separated by a space. This key is stored as a single string in the pages list metadata, associated with the normalized page path (`collection_name/normalized_filename_without_extension`).
## Metadata Key Format
As described in `specs/metadata_structure.md`, the key stored in the pages list metadata for each page will be a single string with the following format:
`blake_hash encrypted_ipfs_hash`
For example:
`b2b6d... Qm...`
The Blake hash has a fixed size depending on the specific Blake variant used. This fixed size can be used to reliably separate the Blake hash from the IPFS hash within the combined key string.
This format allows the browser-hosted website to:
1. Parse the key string to extract the `blake_hash` and `encrypted_ipfs_hash`.
2. Retrieve the encrypted content from IPFS using the `encrypted_ipfs_hash`.
3. Decrypt the retrieved content using the `blake_hash` as the encryption key.
## Security Implications
- **Confidentiality:** Encrypting the content ensures that only someone with the correct Blake hash (the key) can decrypt and view the original content.
- **Integrity:** The IPFS hash of the encrypted content guarantees that the retrieved encrypted data has not been tampered with. The Blake hash of the original content provides an additional integrity check after decryption.
- **Key Management:** The security of this system relies heavily on how the Blake hash (the encryption key) is managed and distributed. While the hash is stored in the public metadata, the assumption is that the original content is not easily guessable or brute-forceable from its Blake hash alone, and that the encryption scheme is strong.
This processing pipeline ensures that content is securely stored and retrieved, leveraging the decentralized and immutable nature of IPFS while adding a layer of confidentiality through encryption.

View File

@ -0,0 +1,36 @@
# IPFS Content Retrieval
This document details the process of retrieving website content (markdown pages and images) from the IPFS network using a client-side IPFS library within the browser.
## Mechanism
The website relies on a JavaScript IPFS client library (e.g., `ipfs-http-client` or `js-ipfs`) running in the user's browser to interact with the IPFS network. Content is retrieved using its Content Identifier (CID), which is a hash of the content itself.
## Retrieval Process
1. **Obtaining the CID:** The IPFS hash (CID) for a specific piece of content (a markdown file or an image) is obtained from the metadata files, specifically the pages list metadata for markdown files. For images referenced within markdown, the CID needs to be resolved as described in the "Handling Image References in Markdown" section.
2. **Initializing the IPFS Client:** The browser-hosted website code initializes the IPFS client library, connecting to a local IPFS node (if available) or a public IPFS gateway.
3. **Fetching Content:** Using the obtained CID, the IPFS client's API is called to fetch the content. The specific API call will depend on the chosen IPFS client library (e.g., a method equivalent to `ipfs.cat(cid)` for file content).
4. **Processing Content:**
- **Markdown:** The retrieved markdown content (as a string or byte array) is processed by a markdown rendering library to convert it into HTML for display in the browser.
- **Images:** The retrieved image data is processed and displayed in an `<img>` tag. This typically involves creating a Blob URL or converting the image data to a Data URL that can be set as the `src` attribute of the `<img>` tag.
## Handling Image References in Markdown
Markdown files can contain image references. Since images are also stored on IPFS within the same collection, these references need to be resolved correctly.
- **Approach 1: Relative Paths:** Image references in markdown could use relative paths within the collection structure (e.g., `../images/image_name.png`). The website code would need to interpret these relative paths in the context of the current page's collection and resolve them to the correct image CID using a mechanism like an asset manifest or by convention (e.g., assuming image filenames correspond to CIDs in a separate lookup).
- **Approach 2: Direct CID References:** Image references in markdown could directly embed the IPFS CID of the image (e.g., `![alt text](ipfs://Qm...)`). This simplifies retrieval but makes the markdown less human-readable and tightly couples it to IPFS CIDs.
- **Approach 3: Asset Manifest:** A separate JSON file within each collection (or a global asset manifest) could map image filenames to their IPFS CIDs. When rendering markdown, the website would look up image filenames in this manifest to get the correct CID for retrieval.
Approach 3 (Asset Manifest) is recommended for better organization and maintainability.
## Error Handling
The IPFS retrieval process should include robust error handling for cases such as:
- IPFS node not available or connection issues.
- CID not found on the network.
- Timeout during content retrieval.
Appropriate feedback should be provided to the user in case of retrieval failures.

179
specs/metadata_structure.md Normal file
View File

@ -0,0 +1,179 @@
# Website Metadata Structure
This document specifies the JSON structures for the metadata files used to define the website's header, footer, navbar, and the list of pages. These metadata files are stored on IPFS and retrieved by the browser-hosted website.
## Header Metadata
The header metadata is a simple JSON structure. Based on the provided image, it includes a logo and potentially links.
```json
{
"logo": {
"alt_text": "OurWorld Logo",
"ipfs_hash": "Qm..." // IPFS hash of the logo image
},
"links": [
{
"text": "ThreeFold.io",
"url": "https://threefold.io",
"external": true
},
{
"text": "Mycelium Network",
"url": "https://mycelium.network",
"external": true
},
{
"text": "AI Box",
"url": "https://aibox.com", // Example URL
"external": true
}
]
}
```
- `logo`: An object containing details about the website logo.
- `alt_text`: Alternative text for the logo image.
- `ipfs_hash`: The IPFS hash (CID) of the logo image file.
- `links`: An array of link objects to be displayed in the header.
- `text`: The display text for the link.
- `url`: The URL the link points to.
- `external`: A boolean indicating if the link opens in a new tab.
## Footer Metadata
The footer metadata is a JSON structure defining the content and links in the website's footer.
```json
{
"text": "© 2024 OurWorld. All rights reserved.",
"links": [
{
"text": "Privacy Policy",
"page_path": "legal/privacy_policy" // Reference to a page in the pages list
},
{
"text": "Terms of Service",
"page_path": "legal/terms_of_service" // Reference to a page in the pages list
}
],
"social_links": [
{
"platform": "twitter",
"url": "https://twitter.com/ourworld"
},
{
"platform": "github",
"url": "https://github.com/ourworld"
}
]
}
```
- `text`: Copyright or other textual information in the footer.
- `links`: An array of internal links, referencing pages defined in the pages list.
- `text`: The display text for the link.
- `page_path`: The unique path of the page as defined in the pages list metadata (`collection/filename`).
- `social_links`: An array of links to social media profiles.
- `platform`: The name of the social media platform (e.g., "twitter", "github").
- `url`: The URL of the social media profile.
## Navbar Metadata
The navbar metadata is a JSON structure defining the website's navigation menu.
```json
{
"menu_items": [
{
"text": "Introduction",
"page_path": "introduction/introduction" // Reference to a page in the pages list
},
{
"text": "Our Story",
"page_path": "about/our_story" // Reference to a page in the pages list
},
{
"text": "Team",
"page_path": "about/team" // Reference to a page in the pages list
},
{
"text": "Collections",
"submenu": [
{
"text": "Collection One",
"page_path": "collections/collection_one_overview" // Reference to a page
},
{
"text": "Collection Two",
"page_path": "collections/collection_two_overview" // Reference to a page
}
]
}
]
}
```
- `menu_items`: An array of menu items. Each item can be a direct link to a page or contain a submenu.
- `text`: The display text for the menu item.
- `page_path`: (Optional) The unique path of the page if it's a direct link.
- `submenu`: (Optional) An array of nested menu items.
## Pages List Metadata
The pages list metadata is a JSON array listing all available markdown pages and the key required to retrieve and decrypt their content.
```json
[
{
"path": "introduction/introduction",
"collection": "introduction",
"filename": "introduction.md",
"key": "... Qm..." // blake_hash encrypted_ipfs_hash
},
{
"path": "about/our_story",
"collection": "about",
"filename": "our_story.md",
"key": "... Qm..."
},
{
"path": "about/team",
"collection": "about",
"filename": "team.md",
"key": "... Qm..."
},
{
"path": "collections/collection_one_overview",
"collection": "collections",
"filename": "collection_one_overview.md",
"key": "... Qm..."
},
{
"path": "collections/collection_two_overview",
"collection": "collections",
"filename": "collection_two_overview.md",
"key": "... Qm..."
},
{
"path": "legal/privacy_policy",
"collection": "legal",
"filename": "privacy_policy.md",
"key": "... Qm..."
},
{
"path": "legal/terms_of_service",
"collection": "legal",
"filename": "terms_of_service.md",
"key": "... Qm..."
}
]
```
- Each object in the array represents a single markdown page.
- `path`: The unique path used to reference the page (e.g., in navbar or footer links). This is a combination of `collection/filename` without the `.md` extension.
- `collection`: The name of the collection (folder) the markdown file belongs to.
- `filename`: The name of the markdown file (including the `.md` extension).
- `key`: A string combining the Blake hash of the original content and the IPFS hash (CID) of the encrypted content, separated by a space. The Blake hash serves as the encryption key.
All names (`collection`, `filename`) are in lowercase and snake_case.

43
specs/page_structure.md Normal file
View File

@ -0,0 +1,43 @@
# Page and Collection Structure
This document describes the organization of markdown pages and associated image files into collections, and how these files are addressed within the website.
## Collections
A collection is a logical grouping of related content, typically consisting of one or more markdown files and associated image files. Collection directories are identified by the presence of a `.collection` file in the root of the directory.
On IPFS, a collection is represented as a directory (or an IPFS object acting as a directory).
- Each collection resides in its own "folder" on IPFS.
- All files directly within a collection directory (excluding subfolders) are considered part of the collection.
- All filenames (markdown and images) within a single collection must be unique.
- Subfolders within a collection are ignored for the purpose of page addressing and content retrieval as defined in the pages list metadata.
## Page Addressing
Markdown pages are addressed using a combination of their collection name and filename. This forms a unique path that is used in the navigation metadata (navbar and footer) and the pages list metadata.
The format for a page path is `collection_name/filename_without_extension`.
For example, a markdown file named `introduction.md` within a collection named `introduction` would have a page path of `introduction/introduction`.
## File Naming and Normalization
All collection names and filenames (for both markdown and images) must adhere to the following conventions:
- **Lowercase:** All characters must be lowercase.
- **Snake Case:** Words should be separated by underscores (`_`).
Before processing, all filenames within a collection directory must be normalized to meet these conventions.
Examples:
- Valid collection names: `introduction`, `our_story`, `legal`
- Valid filenames: `introduction.md`, `privacy_policy.md`, `team_photo.png`
## Pages List and File Resolution
The pages list metadata (`pages_list.json`) serves as the central index for all available markdown pages. Each entry in this list maps a unique page path (`collection/filename_without_extension`) to the IPFS hash of the corresponding markdown file.
When the website needs to display a page based on its page path (e.g., from a navbar link), it looks up the path in the pages list to retrieve the IPFS hash of the markdown file.
Image files associated with a markdown page are expected to reside within the same collection (IPFS directory). Resolving image references within a markdown file to their specific IPFS hashes is handled separately, as described in the `specs/content_retrieval.md` document (preferably using an asset manifest within the collection).

View File

@ -0,0 +1,41 @@
# Security Considerations for a Browser-Hosted IPFS Website
Hosting a website directly from the browser and serving content from IPFS introduces unique security considerations compared to traditional server-hosted websites. This document outlines key security aspects and how they are addressed in this architecture.
## Content Integrity and Immutability
- **Benefit of IPFS:** IPFS is content-addressed, meaning the address (CID) of a piece of content is a cryptographic hash of the content itself. This ensures content integrity. If even a single bit of the content changes, its CID will change, making unauthorized modification immediately detectable.
- **Security Implication:** Users can be confident that the content they retrieve from a given CID is exactly the content that was originally published with that CID. This prevents tampering with individual content files (markdown, images) after they have been added to IPFS.
## Metadata Integrity
- **Challenge:** While individual content files are immutable, the metadata files (header, footer, navbar, pages list) define the structure and links of the website. If these metadata files are tampered with, the website could be altered to display malicious content or link to malicious sites, even if the linked content itself is untampered.
- **Mitigation:** The IPFS hashes of the metadata files themselves must be obtained through a trusted channel. This could be:
- Hardcoding the root metadata hash into the initial website code (less flexible).
- Retrieving the root metadata hash from a trusted source (e.g., a small, verifiable smart contract on a blockchain, a DNSLink entry, or a trusted announcement channel).
- Using IPNS (InterPlanetary Naming System), which allows mutable names that resolve to immutable IPFS CIDs. The IPNS name would need to be obtained through a trusted channel initially.
## Browser Security Model
- **Sandboxing:** The website runs within the browser's security sandbox, which provides a layer of protection against malicious code affecting the user's system.
- **Same-Origin Policy:** The browser's same-origin policy prevents the website's code from making arbitrary requests to other domains, limiting potential cross-site scripting (XSS) and other web vulnerabilities.
## IPFS Client Security
- **Trusted Library:** The JavaScript IPFS client library used in the browser must be a trusted and well-vetted library to avoid introducing vulnerabilities.
- **Gateway vs. Local Node:** Connecting to a public IPFS gateway might expose user IP addresses to the gateway operator. Connecting to a local IPFS node provides more privacy but requires the user to run their own node. The website should ideally support both options.
## Content Rendering Security
- **Markdown Rendering:** The markdown rendering library used to convert markdown to HTML must be secure and prevent the execution of malicious scripts embedded within markdown content (e.g., through injected `<script>` tags or malicious HTML attributes). The rendering process should sanitize the output HTML.
- **Image Handling:** Care must be taken when handling retrieved image data to prevent vulnerabilities related to image parsing or display. Using standard browser image handling mechanisms is generally safer than custom parsing.
## External Links
- Links to external websites (not on IPFS or within the defined metadata structure) should be clearly indicated to the user (e.g., with an external link icon) as their content is outside the scope of the IPFS integrity guarantees.
## Initial Code Loading
- The mechanism by which the initial website HTML, CSS, and JavaScript code is loaded into the browser is critical. If this initial load is compromised, the entire security model can be bypassed. This initial loading mechanism (e.g., serving from a traditional web server, a CDN, or even IPFS with a trusted hash) must be secure.
By carefully considering these security aspects and implementing appropriate mitigations, a secure and resilient browser-hosted website leveraging IPFS can be built.

24
sweb/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
sweb/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["svelte.svelte-vscode"]
}

47
sweb/README.md Normal file
View File

@ -0,0 +1,47 @@
# Svelte + TS + Vite
This template should help get you started developing with Svelte and TypeScript in Vite.
## Recommended IDE Setup
[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
## Need an official Svelte framework?
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
## Technical considerations
**Why use this over SvelteKit?**
- It brings its own routing solution which might not be preferable for some users.
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
**Why include `.vscode/extensions.json`?**
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
**Why enable `allowJs` in the TS template?**
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
**Why is HMR not preserving my local component state?**
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
```ts
// store.ts
// An extremely simple external store
import { writable } from 'svelte/store'
export default writable(0)
```

225
sweb/bun.lock Normal file
View File

@ -0,0 +1,225 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "sweb",
"dependencies": {
"lucide-svelte": "^0.509.0",
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tsconfig/svelte": "^5.0.4",
"autoprefixer": "^10.4.21",
"postcss": "^8.5.3",
"svelte": "^5.28.1",
"svelte-check": "^4.1.6",
"tailwindcss": "^4.1.6",
"typescript": "~5.8.3",
"vite": "^6.3.5",
},
},
},
"packages": {
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.40.2", "", { "os": "android", "cpu": "arm" }, "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.40.2", "", { "os": "android", "cpu": "arm64" }, "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.40.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.40.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.40.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.40.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.40.2", "", { "os": "linux", "cpu": "arm" }, "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.40.2", "", { "os": "linux", "cpu": "arm" }, "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.40.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.40.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg=="],
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.40.2", "", { "os": "linux", "cpu": "none" }, "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw=="],
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.40.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.40.2", "", { "os": "linux", "cpu": "none" }, "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.40.2", "", { "os": "linux", "cpu": "none" }, "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.40.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.40.2", "", { "os": "linux", "cpu": "x64" }, "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.40.2", "", { "os": "linux", "cpu": "x64" }, "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.40.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.40.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.40.2", "", { "os": "win32", "cpu": "x64" }, "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA=="],
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.0.3", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.0", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.15", "vitefu": "^1.0.4" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw=="],
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="],
"@tsconfig/svelte": ["@tsconfig/svelte@5.0.4", "", {}, "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q=="],
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
"acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
"autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"browserslist": ["browserslist@4.24.5", "", { "dependencies": { "caniuse-lite": "^1.0.30001716", "electron-to-chromium": "^1.5.149", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw=="],
"caniuse-lite": ["caniuse-lite@1.0.30001717", "", {}, "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw=="],
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"electron-to-chromium": ["electron-to-chromium@1.5.151", "", {}, "sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA=="],
"esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
"esrap": ["esrap@1.4.6", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw=="],
"fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
"lucide-svelte": ["lucide-svelte@0.509.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-6U83jZ0RKvLYLGdx/hTqZyWcquwApQ2Q1E5bKFELXtOw7g8dk1P0qwbAQqs1fqWAtpNevtXTpgShHv/yWAcbjQ=="],
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
"normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"rollup": ["rollup@4.40.2", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.40.2", "@rollup/rollup-android-arm64": "4.40.2", "@rollup/rollup-darwin-arm64": "4.40.2", "@rollup/rollup-darwin-x64": "4.40.2", "@rollup/rollup-freebsd-arm64": "4.40.2", "@rollup/rollup-freebsd-x64": "4.40.2", "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", "@rollup/rollup-linux-arm-musleabihf": "4.40.2", "@rollup/rollup-linux-arm64-gnu": "4.40.2", "@rollup/rollup-linux-arm64-musl": "4.40.2", "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", "@rollup/rollup-linux-riscv64-gnu": "4.40.2", "@rollup/rollup-linux-riscv64-musl": "4.40.2", "@rollup/rollup-linux-s390x-gnu": "4.40.2", "@rollup/rollup-linux-x64-gnu": "4.40.2", "@rollup/rollup-linux-x64-musl": "4.40.2", "@rollup/rollup-win32-arm64-msvc": "4.40.2", "@rollup/rollup-win32-ia32-msvc": "4.40.2", "@rollup/rollup-win32-x64-msvc": "4.40.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg=="],
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"svelte": ["svelte@5.28.2", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.6", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg=="],
"svelte-check": ["svelte-check@4.1.7", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-1jX4BzXrQJhC/Jt3SqYf6Ntu//vmfc6VWp07JkRfK2nn+22yIblspVUo96gzMkg0Zov8lQicxhxsMzOctwcMQQ=="],
"tailwindcss": ["tailwindcss@4.1.6", "", {}, "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg=="],
"tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
"vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
"vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="],
"zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="],
}
}

13
sweb/index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

26
sweb/package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "sweb",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tsconfig/svelte": "^5.0.4",
"autoprefixer": "^10.4.21",
"postcss": "^8.5.3",
"svelte": "^5.28.1",
"svelte-check": "^4.1.6",
"tailwindcss": "^4.1.6",
"typescript": "~5.8.3",
"vite": "^6.3.5"
},
"dependencies": {
"lucide-svelte": "^0.509.0"
}
}

6
sweb/postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

1
sweb/public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

47
sweb/src/App.svelte Normal file
View File

@ -0,0 +1,47 @@
<script lang="ts">
import svelteLogo from './assets/svelte.svg'
import viteLogo from '/vite.svg'
import Counter from './lib/Counter.svelte'
</script>
<main>
<div>
<a href="https://vite.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} class="logo" alt="Vite Logo" />
</a>
<a href="https://svelte.dev" target="_blank" rel="noreferrer">
<img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />
</a>
</div>
<h1>Vite + Svelte</h1>
<div class="card">
<Counter />
</div>
<p>
Check out <a href="https://github.com/sveltejs/kit#readme" target="_blank" rel="noreferrer">SvelteKit</a>, the official Svelte app framework powered by Vite!
</p>
<p class="read-the-docs">
Click on the Vite and Svelte logos to learn more
</p>
</main>
<style>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.svelte:hover {
filter: drop-shadow(0 0 2em #ff3e00aa);
}
.read-the-docs {
color: #888;
}
</style>

87
sweb/src/app.css Normal file
View File

@ -0,0 +1,87 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,49 @@
<script lang="ts">
import type { NavItem } from '../types/nav';
import * as Accordion from '$lib/components/ui/accordion'; // Assuming Shadcn Accordion is in $lib/components/ui/accordion
import { Button } from '$lib/components/ui/button'; // Assuming Shadcn Button is in $lib/components/ui/button
import { ChevronDown } from 'lucide-svelte'; // Assuming lucide-svelte is installed for icons
export let navData: NavItem[];
function renderNavItem(item: NavItem): any {
if (item.children && item.children.length > 0) {
return Accordion.Item;
} else {
return 'a'; // Render as a link
}
}
</script>
<nav class="w-64 bg-gray-800 text-white h-full">
<div class="p-4">
<h2 class="text-xl font-semibold mb-4">OurWorld</h2>
</div>
<Accordion.Root class="w-full">
{#each navData as item}
{#if item.children && item.children.length > 0}
<Accordion.Item value={item.label}>
<Accordion.Trigger class="flex justify-between items-center w-full text-left p-4 hover:bg-gray-700">
{item.label}
<ChevronDown class="h-4 w-4 transition-transform duration-200" />
</Accordion.Trigger>
<Accordion.Content>
<ul class="pl-4">
{#each item.children as child}
<li>
<a href={child.link} class="block p-2 hover:bg-gray-700">
{child.label}
</a>
</li>
{/each}
</ul>
</Accordion.Content>
</Accordion.Item>
{:else}
<a href={item.link} class="block p-4 hover:bg-gray-700">
{item.label}
</a>
{/if}
{/each}
</Accordion.Root>
</nav>

View File

@ -0,0 +1,30 @@
<script lang="ts">
import type { NavItem } from '../types/nav';
import { onMount } from 'svelte';
let navData: NavItem[] = [];
let loading = true;
let error: string | null = null;
onMount(async () => {
try {
const response = await fetch('/src/data/navData.json');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
navData = await response.json();
} catch (e: any) {
error = e.message;
} finally {
loading = false;
}
});
</script>
{#if loading}
<p>Loading navigation data...</p>
{:else if error}
<p>Error loading navigation data: {error}</p>
{:else}
<slot {navData}></slot>
{/if}

View File

@ -0,0 +1,72 @@
[
{
"label": "Introduction",
"link": "/introduction"
},
{
"label": "Why OurWorld",
"link": "/why-ourworld"
},
{
"label": "Our Story",
"link": "/our-story",
"children": [
{
"label": "Story",
"link": "/our-story/story"
},
{
"label": "OurWorld Venture Creator",
"link": "/our-story/venture-creator"
},
{
"label": "Team",
"link": "/our-story/team"
}
]
},
{
"label": "Our Solutions",
"link": "/our-solutions",
"children": [
{
"label": "Intro",
"link": "/our-solutions/intro"
},
{
"label": "SuperBrain",
"link": "/our-solutions/superbrain"
},
{
"label": "Augmented Collective Intelligence",
"link": "/our-solutions/augmented-collective-intelligence"
},
{
"label": "Tier-S Datacenters",
"link": "/our-solutions/tier-s-datacenters"
},
{
"label": "Sovereign internet",
"link": "/our-solutions/sovereign-internet"
},
{
"label": "Antidote Cyber Pandemic",
"link": "/our-solutions/antidote-cyber-pandemic"
},
{
"label": "Earth Regenerator",
"link": "/our-solutions/earth-regenerator"
}
]
},
{
"label": "Our Projects",
"link": "/our-projects",
"children": []
},
{
"label": "More Info",
"link": "/more-info",
"children": []
}
]

View File

@ -0,0 +1,10 @@
<script lang="ts">
let count: number = $state(0)
const increment = () => {
count += 1
}
</script>
<button onclick={increment}>
count is {count}
</button>

9
sweb/src/main.ts Normal file
View File

@ -0,0 +1,9 @@
import { mount } from 'svelte'
import './app.css'
import App from './App.svelte'
const app = mount(App, {
target: document.getElementById('app')!,
})
export default app

5
sweb/src/types/nav.ts Normal file
View File

@ -0,0 +1,5 @@
export interface NavItem {
label: string;
link: string;
children?: NavItem[];
}

2
sweb/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />

7
sweb/svelte.config.js Normal file
View File

@ -0,0 +1,7 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
// for more information about preprocessors
preprocess: vitePreprocess(),
}

10
sweb/tailwind.config.js Normal file
View File

@ -0,0 +1,10 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./src/**/*.{html,js,svelte,ts}',
],
theme: {
extend: {},
},
plugins: [],
}

23
sweb/tsconfig.app.json Normal file
View File

@ -0,0 +1,23 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable checkJs if you'd like to use dynamic types in JS.
* Note that setting allowJs false does not prevent the use
* of JS in `.svelte` files.
*/
"allowJs": true,
"checkJs": true,
"isolatedModules": true,
"moduleDetection": "force",
"paths": {
"$lib/components/ui/*": ["./src/lib/components/ui/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
}

7
sweb/tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

25
sweb/tsconfig.node.json Normal file
View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

7
sweb/vite.config.ts Normal file
View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
// https://vite.dev/config/
export default defineConfig({
plugins: [svelte()],
})