### Sign In Route Setup (TypeScript/JSX) Source: https://feature-sliced.design/docs/get-started/tutorial Sets up the '/login' route for the Remix application. It imports the SignInPage UI component and the signIn API action, exporting the action to manage the login process. ```typescript import { SignInPage, signIn } from "pages/sign-in"; export { signIn as action }; export default SignInPage; ``` -------------------------------- ### Get Example Data API Route (App Router) Source: https://feature-sliced.design/docs/guides/tech/with-nextjs Implements an API route handler for the App Router to fetch example data. It imports a function from the 'shared/db' layer and returns the data as a JSON response. Includes error handling for server-side issues. ```typescript import { getExamplesList } from '@/shared/db'; export const getExampleData = () => { try { const examplesList = getExamplesList(); return Response.json({ examplesList }); } catch { return Response.json(null, { status: 500, statusText: 'Ouch, something went wrong', }); } }; ``` -------------------------------- ### Register Route Setup (TypeScript/JSX) Source: https://feature-sliced.design/docs/get-started/tutorial Configures the '/register' route in a Remix application. It imports the RegisterPage UI component and the register API action, exporting the action to handle POST requests for registration. ```typescript import { RegisterPage, register } from "pages/sign-in"; export { register as action }; export default RegisterPage; ``` -------------------------------- ### Setup Fetch API Client Source: https://feature-sliced.design/docs/guides/examples/api-requests Centralizes HTTP request setup using the native Fetch API. It provides a reusable `post` method that handles JSON serialization, base URL, and default headers. Other methods like PUT, DELETE can be added. ```typescript export const client = { async post(endpoint: string, body: any, options?: RequestInit) { const response = await fetch(`https://your-api-domain.com/api${endpoint}`, { method: 'POST', body: JSON.stringify(body), ...options, headers: { 'Content-Type': 'application/json', 'X-Custom-Header': 'my-custom-value', ...options?.headers, }, }); return response.json(); } // ... other methods like put, delete, etc. }; ``` -------------------------------- ### Setup Axios API Client Source: https://feature-sliced.design/docs/guides/examples/api-requests Centralizes HTTP request setup using Axios. It configures the base URL, timeout, and default headers for API calls. This setup is reusable across the application. ```typescript import axios from 'axios'; export const client = axios.create({ baseURL: 'https://your-api-domain.com/api/', timeout: 5000, headers: { 'X-Custom-Header': 'my-custom-value' } }); ``` -------------------------------- ### Environment Variable Configuration (.env) Source: https://feature-sliced.design/docs/get-started/tutorial Example of a .env file for setting environment variables. Specifically shows the `SESSION_SECRET` variable, which is crucial for the application's authentication and session management. The value should be a long, random string. ```dotenv SESSION_SECRET=dontyoudarecopypastethis ``` -------------------------------- ### Export GET Method for Example API Route (App Router) Source: https://feature-sliced.design/docs/guides/tech/with-nextjs Exports the 'getExampleData' function as the GET handler for an API route in the App Router. This allows Next.js to recognize and execute the handler for incoming GET requests to the '/api/example' endpoint. ```typescript export { getExampleData as GET } from '@/app/api-routes'; ``` -------------------------------- ### Get Example Data API Route (Pages Router) Source: https://feature-sliced.design/docs/guides/tech/with-nextjs Defines an API route handler for the Pages Router, including configuration for request body parsing and maximum duration. It sets up a basic handler that responds with a JSON message. ```typescript import type { NextApiRequest, NextApiResponse } from 'next'; const config = { api: { bodyParser: { sizeLimit: '1mb', }, }, maxDuration: 5, }; const handler = (req: NextApiRequest, res: NextApiResponse) => { res.status(200).json({ message: 'Hello from FSD' }); }; export const getExampleData = { config, handler } as const; ``` -------------------------------- ### Page Component Example (JavaScript) Source: https://feature-sliced.design/docs/guides/migration/from-custom A basic React functional component example for a page. This component serves as a placeholder and can be expanded with specific UI elements and logic. ```javascript export function ProductPage(props) { return
; } ``` -------------------------------- ### Route File Example (JavaScript) Source: https://feature-sliced.design/docs/guides/migration/from-custom This JavaScript code snippet demonstrates how a route file might be structured to export a page component. It assumes the use of an alias '@' for the 'src' directory. ```javascript export { ProductPage as default } from "@/pages/product" ``` -------------------------------- ### Create and Route a Home Page in SvelteKit with FSD Source: https://feature-sliced.design/docs/guides/tech/with-sveltekit This example demonstrates how to create a page slice for a home page using the FSD CLI and then integrate it into the SvelteKit routing. It involves creating a public API export for the page component and defining the corresponding route in the `src/app/routes` directory. This ensures the page is accessible and correctly rendered within the application. ```typescript export { default as HomePage } from './ui/home-page.svelte'; ``` ```svelte ``` -------------------------------- ### Page Index File Example (JavaScript) Source: https://feature-sliced.design/docs/guides/migration/from-custom This JavaScript code snippet shows a typical index file for a page within the 'pages' directory. It exports the main page component, facilitating imports from other parts of the application. ```javascript export { ProductPage } from "./ProductPage.jsx" ``` -------------------------------- ### Integrate Article Editor with Remix Routes (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial These route files (for both index and dynamic slug routes) import and export the necessary components and functions from the article edit feature module. This setup allows the same editor component to handle both new article creation and existing article editing. ```typescript import { ArticleEditPage } from "pages/article-edit"; export { loader, action } from "pages/article-edit"; export default ArticleEditPage; ``` -------------------------------- ### Configure SvelteKit for FSD Structure Source: https://feature-sliced.design/docs/guides/tech/with-sveltekit This configuration modifies SvelteKit's default file paths to align with Feature Sliced Design principles. It moves routing to the `src/app` layer and adjusts the location of the application template and library files. This setup is crucial for maintaining a consistent FSD structure within a SvelteKit project. ```typescript import adapter from '@sveltejs/adapter-auto'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config}*/ const config = { preprocess: [vitePreprocess()], kit: { adapter: adapter(), files: { routes: 'src/app/routes', // move routing inside the app layer lib: 'src', appTemplate: 'src/app/index.html', // Move the application entry point inside the app layer assets: 'public' }, alias: { '@/*': 'src/*' // Create an alias for the src directory } } }; export default config; ``` -------------------------------- ### Run Steiger Linter for FSD Slice Analysis Source: https://feature-sliced.design/docs/guides/migration/from-v2-0 This command executes the Steiger linter on the 'src' directory to identify potential issues with slice granularity according to FSD v2.1 principles. It helps in finding insignificant slices or excessive slicing, suggesting merges for better project navigation and maintainability. ```bash npx steiger src ``` -------------------------------- ### Create Shared UI Directory Source: https://feature-sliced.design/docs/get-started/tutorial This command initializes a new directory structure for shared UI components using the fsd CLI. It's a foundational step for organizing reusable UI elements within the application. ```bash npx fsd shared ui ``` -------------------------------- ### Root Layout with Header and Context Provider (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Sets up the root application layout using Remix. It includes the Header component, provides the CurrentUser context, and defines a loader function to fetch user data from session cookies. This ensures the header and user data are available globally. ```typescript import { cssBundleHref } from "@remix-run/css-bundle"; import type { LinksFunction, LoaderFunctionArgs } from "@remix-run/node"; import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData, } from "@remix-run/react"; import { Header } from "shared/ui"; import { getUserFromSession, CurrentUser } from "shared/api"; export const links: LinksFunction = () => [ ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), ]; export const loader = ({ request }: LoaderFunctionArgs) => getUserFromSession(request); export default function App() { ``` -------------------------------- ### Create Typed API Client (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Creates a typed API client using the 'openapi-fetch' package. It imports necessary types from './v1' and configures the client with the backend base URL. ```typescript import createClient from "openapi-fetch"; import { backendBaseUrl } from "shared/config"; import type { paths } from "./v1"; export const { GET, POST, PUT, DELETE } = createClient({ baseUrl: backendBaseUrl }); ``` -------------------------------- ### Export API Client Methods (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Exports the HTTP method functions (GET, POST, PUT, DELETE) from the API client. This allows other modules to import and use these methods for making API requests. ```typescript export { GET, POST, PUT, DELETE } from "./client"; ``` -------------------------------- ### Create Article Reader API Loader Source: https://feature-sliced.design/docs/get-started/tutorial Sets up the API loader for the article reader page. It fetches article details and comments using the provided slug, handling potential errors and user authentication. Dependencies include Remix, tiny-invariant, openapi-fetch, and remix-utils. ```typescript import { json, type LoaderFunctionArgs } from "@remix-run/node"; import invariant from "tiny-invariant"; import type { FetchResponse } from "openapi-fetch"; import { promiseHash } from "remix-utils/promise"; import { GET, getUserFromSession } from "shared/api"; async function throwAnyErrors( responsePromise: Promise>, ) { const { data, error, response } = await responsePromise; if (error !== undefined) { throw json(error, { status: response.status }); } return data as NonNullable; } export const loader = async ({ request, params }: LoaderFunctionArgs) => { invariant(params.slug, "Expected a slug parameter"); const currentUser = await getUserFromSession(request); const authorization = currentUser ? { Authorization: `Token ${currentUser.token}` } : undefined; return json( await promiseHash({ article: throwAnyErrors( GET("/articles/{slug}", { params: { path: { slug: params.slug }, }, headers: authorization, }), ), comments: throwAnyErrors( GET("/articles/{slug}/comments", { params: { path: { slug: params.slug }, }, headers: authorization, }), ), }), ); }; ``` -------------------------------- ### Export API Client and Article Type (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Exports the HTTP methods (GET, POST, PUT, DELETE) from the API client and re-exports the 'Article' type from the models file. This provides a centralized point for API-related imports. ```typescript export { GET, POST, PUT, DELETE } from "./client"; export type { Article } from "./models"; ``` -------------------------------- ### Generate Page Components with fsd CLI Source: https://feature-sliced.design/docs/get-started/tutorial This command uses the fsd CLI to generate boilerplate components for multiple pages within the 'pages' layer, specifically under the 'ui' segment. It helps in quickly setting up the basic file structure for new pages. ```bash npx fsd pages feed sign-in article-read article-edit profile settings --segments ui ``` -------------------------------- ### Configure NuxtJS Alias for Src Directory Source: https://feature-sliced.design/docs/guides/tech/with-nuxtjs Sets up an alias '@' to point to the 'src' directory in the NuxtJS configuration. This is crucial for referencing modules within the 'src' folder when using FSD principles. It's a common setup step for FSD in NuxtJS projects. ```typescript export default defineNuxtConfig({ devtools: { enabled: true }, // Not FSD related, enabled at project startup alias: { "@": '../src' }, }) ``` -------------------------------- ### Generate API Client and Config Segments (FSD) Source: https://feature-sliced.design/docs/get-started/tutorial This command uses the fsd CLI to create 'api' and 'config' segments within the 'shared' directory for organizing API client-related code and configuration variables. ```bash npx fsd shared --segments api config ``` -------------------------------- ### Sign In User API Endpoint (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Manages user login by extracting email and password from form data, sending a POST request to the '/users/login' endpoint, and establishing a user session upon successful authentication. It returns an error if login fails. ```typescript import { json, type ActionFunctionArgs } from "@remix-run/node"; import { POST, createUserSession } from "shared/api"; export const signIn = async ({ request }: ActionFunctionArgs) => { const formData = await request.formData(); const email = formData.get("email")?.toString() ?? ""; const password = formData.get("password")?.toString() ?? ""; const { data, error } = await POST("/users/login", { body: { user: { email, password } }, }); if (error) { return json({ error }, { status: 400 }); } else { return createUserSession({ request: request, user: data.user, redirectTo: "/", }); } }; ``` -------------------------------- ### Declare Untyped Package in TypeScript Source: https://feature-sliced.design/docs/guides/examples/types Provides an example of how to declare types for a package that does not provide its own typings. This is done using a `.d.ts` file to inform the TypeScript compiler about the existence of the module, preventing compilation errors. It's a workaround for libraries lacking type definitions. ```typescript // This library doesn't have typings, and we didn't want to bother writing our own. declare module "use-react-screenshot"; ``` -------------------------------- ### Use Query Factory in React Component Source: https://feature-sliced.design/docs/guides/tech/with-react-query This example shows how to integrate a query factory into a React component using react-router-dom and react-query. It fetches post details based on a route parameter and displays the post information or loading/error states. Dependencies include 'react-router-dom' and '@tanstack/react-query'. ```typescript import { useParams } from "react-router-dom"; import { postApi } from "@/entities/post"; import { useQuery } from "@tanstack/react-query"; type Params = { postId: string; }; export const PostPage = () => { const { postId } = useParams(); const id = parseInt(postId || ""); const { data: post, error, isLoading, isError, } = useQuery(postApi.postQueries.detail({ id })); if (isLoading) { return
Loading...
; } if (isError || !post) { return <>{error?.message}; } return (

Post id: {post.id}

{post.title}

{post.body}

Owner: {post.userId}
); }; ``` -------------------------------- ### Standardize Cross-Imports with @x Notation in FSD Source: https://feature-sliced.design/docs/guides/migration/from-v2-0 Demonstrates the new '@x' notation for standardizing cross-imports between entities in Feature-Sliced Design v2.1. This syntax is used when importing types or modules from one entity that are intended for use by another, promoting cleaner import paths. ```typescript import type { EntityA } from "entities/A/@x/B"; ``` -------------------------------- ### Register User API Endpoint (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Handles user registration by processing form data, sending a POST request to the '/users' endpoint, and creating a user session upon successful registration. It returns an error if the API call fails. ```typescript import { json, type ActionFunctionArgs } from "@remix-run/node"; import { POST, createUserSession } from "shared/api"; export const register = async ({ request }: ActionFunctionArgs) => { const formData = await request.formData(); const username = formData.get("username")?.toString() ?? ""; const email = formData.get("email")?.toString() ?? ""; const password = formData.get("password")?.toString() ?? ""; const { data, error } = await POST("/users", { body: { user: { email, password, username } }, }); if (error) { return json({ error }, { status: 400 }); } else { return createUserSession({ request: request, user: data.user, redirectTo: "/", }); } }; ``` -------------------------------- ### Define Song Entity and List Songs API (TypeScript) Source: https://feature-sliced.design/docs/guides/examples/types Defines a 'Song' interface and a 'listSongs' function to fetch and parse song data from an API. It includes type safety for the response using a Promise that resolves to an array of 'Song' objects. This example assumes 'Artist' type is imported from a shared location. ```typescript import type { Artist } from "./artists"; interface Song { id: number; title: string; artists: Array; } export function listSongs() { return fetch('/api/songs').then((res) => res.json() as Promise>); } ``` -------------------------------- ### Define Article Preview Component (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Creates a reusable React component to display a preview of an article. It takes an 'article' object as a prop and renders its title, description, author information, and tags. It uses Remix's Link for navigation. ```typescript import { Link } from "@remix-run/react"; import type { Article } from "shared/api"; interface ArticlePreviewProps { article: Article; } export function ArticlePreview({ article }: ArticlePreviewProps) { return (
{article.author.username} {new Date(article.createdAt).toLocaleDateString(undefined, { dateStyle: "long", })}

{article.title}

{article.description}

Read more...
    {article.tagList.map((tag) => (
  • {tag}
  • ))}
); } ``` -------------------------------- ### TypeScript API Client for Standardized API Calls Source: https://feature-sliced.design/docs/guides/tech/with-react-query A custom `ApiClient` class in TypeScript for managing API requests. It handles base URL configuration, response parsing (JSON), GET and POST requests with optional query parameters and request bodies. It relies on the browser's `fetch` API and a configured `API_URL` from '@/shared/config'. ```typescript import { API_URL } from "@/shared/config"; export class ApiClient { private baseUrl: string; constructor(url: string) { this.baseUrl = url; } async handleResponse(response: Response): Promise { if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } try { return await response.json(); } catch (error) { throw new Error("Error parsing JSON response"); } } public async get( endpoint: string, queryParams?: Record, ): Promise { const url = new URL(endpoint, this.baseUrl); if (queryParams) { Object.entries(queryParams).forEach(([key, value]) => { url.searchParams.append(key, value.toString()); }); } const response = await fetch(url.toString(), { method: "GET", headers: { "Content-Type": "application/json", }, }); return this.handleResponse(response); } public async post>( endpoint: string, body: TData, ): Promise { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(body), }); return this.handleResponse(response); } } export const apiClient = new ApiClient(API_URL); ``` -------------------------------- ### Load Paginated Articles and Tags (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial This loader function fetches articles and tags for a feed, supporting pagination and tag filtering. It uses `openapi-fetch` for API requests and `remix-utils` for promise handling. It returns articles with pagination information and a list of available tags. ```typescript import { json, type LoaderFunctionArgs } from "@remix-run/node"; import type { FetchResponse } from "openapi-fetch"; import { promiseHash } from "remix-utils/promise"; import { GET } from "shared/api"; async function throwAnyErrors( responsePromise: Promise>, ) { const { data, error, response } = await responsePromise; if (error !== undefined) { throw json(error, { status: response.status }); } return data as NonNullable; } /** Amount of articles on one page. */ export const LIMIT = 20; export const loader = async ({ request }: LoaderFunctionArgs) => { const url = new URL(request.url); const selectedTag = url.searchParams.get("tag") ?? undefined; const page = parseInt(url.searchParams.get("page") ?? "", 10); return json( await promiseHash({ articles: throwAnyErrors( GET("/articles", { params: { query: { tag: selectedTag, limit: LIMIT, offset: !Number.isNaN(page) ? page * LIMIT : undefined, }, }, }), ), tags: throwAnyErrors(GET("/tags")), }), ); }; ``` -------------------------------- ### Define Backend Base URL (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Exports the mock backend URL as the backend base URL. This file is part of the shared configuration for the API client. ```typescript export { mockBackendUrl as backendBaseUrl } from "mocks/handlers"; ``` -------------------------------- ### Generate API Types (NPM Script) Source: https://feature-sliced.design/docs/get-started/tutorial Executes an npm script to generate up-to-date API type definitions based on the project's OpenAPI specification. This is crucial for creating a typed API client. ```bash npm run generate-api-types ``` -------------------------------- ### Remix Session Management Utilities (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Server-side utilities for managing user sessions in a Remix application. Includes functions for creating user sessions, retrieving user data from sessions, and requiring user authentication. Depends on '@remix-run/node' and 'tiny-invariant'. Requires `SESSION_SECRET` environment variable. ```typescript import { createCookieSessionStorage, redirect } from "@remix-run/node"; import invariant from "tiny-invariant"; import type { User } from "./models"; invariant( process.env.SESSION_SECRET, "SESSION_SECRET must be set for authentication to work", ); const sessionStorage = createCookieSessionStorage({ cookie: { name: "__session", httpOnly: true, path: "/", sameSite: "lax", secrets: [process.env.SESSION_SECRET], secure: process.env.NODE_ENV === "production", }, }); export async function createUserSession({ request, user, redirectTo, }: { request: Request; user: User; redirectTo: string; }) { const cookie = request.headers.get("Cookie"); const session = await sessionStorage.getSession(cookie); session.set("user", user); return redirect(redirectTo, { headers: { "Set-Cookie": await sessionStorage.commitSession(session, { maxAge: 60 * 60 * 24 * 7, // 7 days }), }, }); } export async function getUserFromSession(request: Request) { const cookie = request.headers.get("Cookie"); const session = await sessionStorage.getSession(cookie); return session.get("user") ?? null; } export async function requireUser(request: Request) { const user = await getUserFromSession(request); if (user === null) { throw redirect("/login"); } return user; } ``` -------------------------------- ### Handle Article Creation and Updates (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial This action function handles both creating new articles and updating existing ones. It parses form data, validates it using a schema, and then sends the data to the backend via POST or PUT requests. It redirects to the article page upon successful submission or returns errors if validation fails. ```typescript import { json, redirect, type ActionFunctionArgs } from "@remix-run/node"; import { POST, PUT, requireUser } from "shared/api"; import { parseAsArticle } from "../model/parseAsArticle"; export const action = async ({ request, params }: ActionFunctionArgs) => { try { const { body, description, title, tags } = parseAsArticle( await request.formData(), ); const tagList = tags?.split(",") ?? []; const currentUser = await requireUser(request); const payload = { body: { article: { title, description, body, tagList, }, }, headers: { Authorization: `Token ${currentUser.token}` }, }; const { data, error } = await (params.slug ? PUT("/articles/{slug}", { params: { path: { slug: params.slug } }, ...payload, }) : POST("/articles", payload)); if (error) { return json({ errors: error }, { status: 422 }); } return redirect(`/article/${data.article.slug ?? ""}`); } catch (errors) { return json({ errors }, { status: 400 }); } }; ``` -------------------------------- ### API Actions for Article Comments and Author Following (TypeScript) Source: https://feature-sliced.design/docs/get-started/tutorial Implements backend API actions for managing comments (POST, DELETE) and user following/unfollowing (POST, DELETE) related to articles. It utilizes `invariant` for validation and `redirectBack` for navigation. Dependencies include `invariant`, `POST`, `DELETE`, and `redirectBack`. ```typescript const comment = formData.get("comment"); invariant(typeof comment === "string", "Expected a comment parameter"); await POST("/articles/{slug}/comments", { params: { path: { slug: params.slug } }, headers: { ...authorization, "Content-Type": "application/json" }, body: { comment: { body: comment } }, }); return redirectBack(request, { fallback: "/" }); }, async deleteComment() { invariant(params.slug, "Expected a slug parameter"); const commentId = formData.get("id"); invariant(typeof commentId === "string", "Expected an id parameter"); const commentIdNumeric = parseInt(commentId, 10); invariant( !Number.isNaN(commentIdNumeric), "Expected a numeric id parameter", ); await DELETE("/articles/{slug}/comments/{id}", { params: { path: { slug: params.slug, id: commentIdNumeric } }, headers: authorization, }); return redirectBack(request, { fallback: "/" }); }, async followAuthor() { const authorUsername = formData.get("username"); invariant( typeof authorUsername === "string", "Expected a username parameter", ); await POST("/profiles/{username}/follow", { params: { path: { username: authorUsername } }, headers: authorization, }); return redirectBack(request, { fallback: "/" }); }, async unfollowAuthor() { const authorUsername = formData.get("username"); invariant( typeof authorUsername === "string", "Expected a username parameter", ); await DELETE("/profiles/{username}/follow", { params: { path: { username: authorUsername } }, headers: authorization, }); return redirectBack(request, { fallback: "/" }); }, }); }; ``` -------------------------------- ### Electron FSD Project Structure Source: https://feature-sliced.design/docs/guides/tech/with-electron Illustrates a typical Feature-Sliced Design directory structure for an Electron application, separating main process, renderer process, and shared code. It highlights the 'app' layer for entry points and the 'shared/ipc' for inter-process communication contracts. ```tree └── src ├── app # Common app layer │ ├── main # Main process │ │ └── index.ts # Main process entry point │ ├── preload # Preload script and Context Bridge │ │ └── index.ts # Preload entry point │ └── renderer # Renderer process │ └── index.html # Renderer process entry point ├── main │ ├── features │ │ └── user │ │ └── ipc │ │ ├── get-user.ts │ │ └── send-user.ts │ ├── entities │ └── shared ├── renderer │ ├── pages │ │ ├── settings │ │ │ ├── ipc │ │ │ │ ├── get-user.ts │ │ │ │ └── save-user.ts │ │ │ ├── ui │ │ │ │ └── user.tsx │ │ │ └── index.ts │ │ └── home │ │ ├── ui │ │ │ └── home.tsx │ │ └── index.ts │ ├── widgets │ ├── features │ ├── entities │ └── shared └── shared # Common code between main and renderer └── ipc # IPC description (event names, contracts) ```