├── .nvmrc ├── .npmrc ├── apps ├── launcher │ ├── i18n │ │ └── en.json │ ├── README.md │ ├── .env │ ├── expo-module.config.json │ ├── src │ │ ├── styles │ │ │ ├── global.css │ │ │ └── theme.ts │ │ ├── utils │ │ │ ├── cn.ts │ │ │ ├── cn.test.ts │ │ │ └── i18n.ts │ │ ├── app │ │ │ ├── (root) │ │ │ │ ├── (startscreen) │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── _layout.tsx │ │ │ │ └── _layout.tsx │ │ │ └── _layout.tsx │ │ ├── @types │ │ │ └── svg.d.ts │ │ ├── hooks │ │ │ ├── use-theme.tsx │ │ │ └── use-color-scheme.tsx │ │ ├── components │ │ │ └── text.tsx │ │ └── providers │ │ │ ├── registry.tsx │ │ │ └── splash-provider.tsx │ ├── assets │ │ ├── images │ │ │ ├── icon.png │ │ │ ├── favicon.png │ │ │ ├── react-logo.png │ │ │ ├── splash-icon.png │ │ │ ├── adaptive-icon.png │ │ │ ├── react-logo@2x.png │ │ │ ├── react-logo@3x.png │ │ │ └── partial-react-logo.png │ │ └── fonts │ │ │ └── SpaceMono-Regular.ttf │ ├── nativewind-env.d.ts │ ├── typedoc.json │ ├── index.ts │ ├── babel.config.cjs │ ├── tsconfig.test.json │ ├── metro.config.cjs │ ├── tsconfig.json │ ├── tailwind.config.ts │ ├── app.config.ts │ └── package.json └── .gitignore ├── .github ├── FUNDING.yml ├── CODEOWNERS ├── img │ └── favicon.png ├── common │ └── bootstrap │ │ └── action.yml ├── workflows │ ├── docs.yml │ └── ci.yml └── ISSUE_TEMPLATE │ └── bug_report.md ├── .husky ├── pre-commit └── commit-msg ├── packages ├── .gitignore └── live-tiles │ ├── src │ ├── utils │ │ ├── index.ts │ │ ├── size.test.ts │ │ └── size.ts │ ├── @types │ │ ├── index.ts │ │ └── optional.ts │ ├── tiles │ │ ├── index.ts │ │ ├── tile-sizes.ts │ │ ├── tile-density.ts │ │ └── live-tiles.ts │ ├── index.ts │ └── visualizer.ts │ ├── typedoc.json │ ├── README.md │ ├── tsconfig.test.json │ ├── tsconfig.json │ ├── tsup.config.js │ ├── jest.config.ts │ └── package.json ├── pnpm-workspace.yaml ├── commitlint.config.cjs ├── tsconfig.test.json ├── .vscode ├── extensions.json └── settings.json ├── .renovaterc ├── .changeset └── config.json ├── .gitignore ├── turbo.json ├── typedoc.json ├── tsconfig.json ├── README.md ├── package.json ├── CONTRIBUTING.md ├── biome.json └── LICENSE /.nvmrc: -------------------------------------------------------------------------------- 1 | 24.x 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | -------------------------------------------------------------------------------- /apps/launcher/i18n/en.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [filiphsps] 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm run precommit 2 | -------------------------------------------------------------------------------- /apps/launcher/README.md: -------------------------------------------------------------------------------- 1 | # AdaptiveShell 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | .github** @filiphsps 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | pnpm exec commitlint --edit $1 2 | -------------------------------------------------------------------------------- /apps/.gitignore: -------------------------------------------------------------------------------- 1 | */.expo/ 2 | */android/ 3 | */ios/ 4 | -------------------------------------------------------------------------------- /packages/.gitignore: -------------------------------------------------------------------------------- 1 | **/.tsup/ 2 | **/dist/ 3 | **/.tsbuildinfo 4 | -------------------------------------------------------------------------------- /packages/live-tiles/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './size'; 2 | -------------------------------------------------------------------------------- /apps/launcher/.env: -------------------------------------------------------------------------------- 1 | RCT_USE_PREBUILT_RNCORE=1 2 | RCT_USE_RN_DEP=1 3 | -------------------------------------------------------------------------------- /packages/live-tiles/src/@types/index.ts: -------------------------------------------------------------------------------- 1 | export type * from './optional'; 2 | -------------------------------------------------------------------------------- /apps/launcher/expo-module.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "platforms": ["apple", "android"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/live-tiles/src/@types/optional.ts: -------------------------------------------------------------------------------- 1 | export type Optional = T | null | undefined; 2 | -------------------------------------------------------------------------------- /.github/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/.github/img/favicon.png -------------------------------------------------------------------------------- /apps/launcher/src/styles/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/launcher/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/images/icon.png -------------------------------------------------------------------------------- /apps/launcher/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/images/favicon.png -------------------------------------------------------------------------------- /apps/launcher/assets/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/images/react-logo.png -------------------------------------------------------------------------------- /packages/live-tiles/src/tiles/index.ts: -------------------------------------------------------------------------------- 1 | export * from './live-tiles'; 2 | export * from './tile-density'; 3 | export * from './tile-sizes'; 4 | -------------------------------------------------------------------------------- /packages/live-tiles/src/tiles/tile-sizes.ts: -------------------------------------------------------------------------------- 1 | export enum LiveTileSize { 2 | Small = 0, 3 | Medium, 4 | Wide, 5 | Large 6 | } 7 | -------------------------------------------------------------------------------- /apps/launcher/assets/images/splash-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/images/splash-icon.png -------------------------------------------------------------------------------- /apps/launcher/assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /apps/launcher/assets/images/react-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/images/react-logo@2x.png -------------------------------------------------------------------------------- /apps/launcher/assets/images/react-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/images/react-logo@3x.png -------------------------------------------------------------------------------- /apps/launcher/assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /apps/launcher/assets/images/partial-react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filiphsps/AdaptiveShell/HEAD/apps/launcher/assets/images/partial-react-logo.png -------------------------------------------------------------------------------- /packages/live-tiles/src/index.ts: -------------------------------------------------------------------------------- 1 | // FIXME: Barrel files is extremely bad practice. 2 | 3 | export * from './tiles'; 4 | export * from './utils'; 5 | export * from './visualizer'; 6 | -------------------------------------------------------------------------------- /apps/launcher/nativewind-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind. 4 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - apps/** 3 | - packages/** 4 | 5 | enablePrePostScripts: true 6 | 7 | nodeLinker: hoisted 8 | 9 | onlyBuiltDependencies: 10 | - esbuild 11 | - unrs-resolver 12 | -------------------------------------------------------------------------------- /apps/launcher/src/utils/cn.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) || undefined; 6 | } 7 | -------------------------------------------------------------------------------- /commitlint.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'subject-case': [2, 'always', 'sentence-case'], 5 | 'subject-full-stop': [2, 'always', '.'] 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /apps/launcher/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://typedoc.org/schema.json", 3 | "name": "Launcher", 4 | "entryPoints": ["./index.ts"], 5 | "readme": "./README.md", 6 | "basePath": "./src", 7 | "projectDocuments": ["./docs/**/*.md"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/live-tiles/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://typedoc.org/schema.json", 3 | "name": "LiveTiles", 4 | "entryPoints": ["src/index.ts"], 5 | "readme": "./README.md", 6 | "basePath": "./src", 7 | "projectDocuments": ["./docs/**/*.md"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/launcher/index.ts: -------------------------------------------------------------------------------- 1 | import '@bacons/text-decoder/install'; 2 | import { configureReanimatedLogger, ReanimatedLogLevel } from 'react-native-reanimated'; 3 | 4 | configureReanimatedLogger({ 5 | level: ReanimatedLogLevel.error, 6 | strict: true 7 | }); 8 | 9 | import 'expo-router/entry'; 10 | -------------------------------------------------------------------------------- /apps/launcher/src/app/(root)/(startscreen)/index.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from 'react-native'; 2 | 3 | export default function StartScreenView() { 4 | return ( 5 | 6 | TODO 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/live-tiles/README.md: -------------------------------------------------------------------------------- 1 | # @metro-ui/live-tiles 2 | 3 | [![npm downloads](https://img.shields.io/npm/dt/@metro-ui/live-tiles.svg)](https://www.npmjs.com/package/@metro-ui/live-tiles) 4 | [![npm version](https://img.shields.io/npm/v/@metro-ui/live-tiles.svg?style=flat)](https://www.npmjs.com/package/@metro-ui/live-tiles) 5 | -------------------------------------------------------------------------------- /apps/launcher/src/@types/svg.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | import React from 'react'; 3 | import { SvgProps } from 'react-native-svg'; 4 | const content: React.FC; 5 | export default content; 6 | } 7 | 8 | declare module '*.png' { 9 | const content: any; 10 | export default content; 11 | } 12 | -------------------------------------------------------------------------------- /packages/live-tiles/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": ["./tsconfig.json", "../../tsconfig.test.json"], 4 | "compilerOptions": { 5 | "types": ["jest"] 6 | }, 7 | "include": ["eslint.config.mjs", "jest.config.ts"], 8 | "exclude": ["build", "node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/launcher/src/app/(root)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import '~/styles/global.css'; 2 | 3 | import { Stack } from 'expo-router'; 4 | 5 | export default function RootLayout() { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "types": ["jest"] 6 | }, 7 | "include": ["app.config.ts", "metro.config.cjs", "src/**/*.test.*", "eslint.config.mjs", "commitlint.config.cjs"], 8 | "exclude": ["build", "node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/launcher/src/app/(root)/(startscreen)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import '~/styles/global.css'; 2 | 3 | import { Stack } from 'expo-router'; 4 | 5 | export default function StartScreenLayout() { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /apps/launcher/src/hooks/use-theme.tsx: -------------------------------------------------------------------------------- 1 | import { useColorScheme } from '~/hooks/use-color-scheme'; 2 | import { DARK_THEME, LIGHT_THEME } from '~/styles/theme'; 3 | 4 | export function useTheme() { 5 | const { colorScheme } = useColorScheme(); 6 | 7 | const activeTheme = colorScheme === 'dark' ? 'dark' : 'light'; 8 | const theme = activeTheme === 'dark' ? DARK_THEME : LIGHT_THEME; 9 | 10 | return { theme }; 11 | } 12 | -------------------------------------------------------------------------------- /apps/launcher/babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(true); 3 | return { 4 | presets: [ 5 | [ 6 | 'babel-preset-expo', 7 | { 8 | jsxImportSource: 'nativewind', 9 | 'react-compiler': {} 10 | } 11 | ], 12 | 'nativewind/babel' 13 | ], 14 | plugins: ['react-native-reanimated/plugin'] 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/live-tiles/src/visualizer.ts: -------------------------------------------------------------------------------- 1 | export function VisualizeLiveTileContent() {} 2 | export function VisualizeLiveTileContentAdaptive() {} 3 | export function VisualizeLiveTileContentTileSquareBlock() {} 4 | export function VisualizeLiveTileContentIconWithBadge() {} 5 | 6 | export function VisualizeLiveTileBranding() {} 7 | export function VisualizeLiveTileBrandingLogo() {} 8 | export function VisualizeLiveTileBrandingName() {} 9 | 10 | export function VisualizeLiveTile() {} 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "bradlc.vscode-tailwindcss", 4 | "mikestead.dotenv", 5 | "orta.vscode-jest", 6 | "expo.vscode-expo-tools", 7 | "dbaeumer.vscode-eslint", 8 | "yoavbls.pretty-ts-errors", 9 | "christian-kohler.path-intellisense", 10 | "christian-kohler.npm-intellisense", 11 | "chdsbd.github-code-owners", 12 | "bierner.jsdoc-markdown-highlighting" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.renovaterc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended", 5 | ":semanticCommitTypeAll(chore)", 6 | ":automergeMajor" 7 | ], 8 | "ignoreDeps": [ 9 | "@testing-library/react-native", 10 | "@types/jest", 11 | "jest", 12 | "react-dom", 13 | "react-native-keyboard-controller", 14 | "react-native-reanimated", 15 | "react-native", 16 | "react", 17 | "tailwindcss" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /apps/launcher/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": ["./tsconfig.json", "../../tsconfig.test.json"], 4 | "compilerOptions": { 5 | "jsx": "react-native", 6 | "types": ["react", "jest"] 7 | }, 8 | "include": [ 9 | "src/**/*.test.*", 10 | "metro.config.cjs", 11 | "eslint.config.mjs", 12 | "tailwind.config.ts", 13 | "babel.config.cjs", 14 | "app.config.ts" 15 | ], 16 | "exclude": ["build", "node_modules"] 17 | } 18 | -------------------------------------------------------------------------------- /.github/common/bootstrap/action.yml: -------------------------------------------------------------------------------- 1 | name: "📦 Bootstrap" 2 | description: "Install and configure dependencies" 3 | 4 | runs: 5 | using: "composite" 6 | steps: 7 | - name: 🏎️ Setup pnpm 8 | uses: pnpm/action-setup@v4 9 | 10 | - name: 🏎️ Setup Node 11 | uses: actions/setup-node@v6 12 | with: 13 | registry-url: "https://registry.npmjs.org/" 14 | node-version-file: ".nvmrc" 15 | cache: "pnpm" 16 | 17 | - name: 📦 Install dependencies 18 | shell: bash 19 | run: pnpm install --frozen-lockfile 20 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@latest/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { 6 | "repo": "filiphsps/AdaptiveShell" 7 | } 8 | ], 9 | "commit": false, 10 | "fixed": [], 11 | "linked": [["{,@metro-ui/}{,*,!launcher}"]], 12 | "access": "public", 13 | "baseBranch": "master", 14 | "updateInternalDependencies": "patch", 15 | "privatePackages": false, 16 | "snapshot": { 17 | "useCalculatedVersion": true 18 | }, 19 | "ignore": ["@metro-ui/launcher"] 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules/ 3 | 4 | # Expo 5 | .expo/ 6 | dist/ 7 | web-build/ 8 | expo-env.d.ts 9 | 10 | # Native 11 | *.orig.* 12 | *.jks 13 | *.p8 14 | *.p12 15 | *.key 16 | *.mobileprovision 17 | 18 | # Metro 19 | .metro-health-check* 20 | 21 | # debug 22 | npm-debug.* 23 | yarn-debug.* 24 | yarn-error.* 25 | coverage/ 26 | coverage-temp/ 27 | 28 | # macOS 29 | .DS_Store 30 | *.pem 31 | 32 | # typescript 33 | *.tsbuildinfo 34 | 35 | # Build 36 | .turbo 37 | android 38 | ios 39 | /docs/ 40 | 41 | notes.txt 42 | project.inlang 43 | 44 | # local env files 45 | .env*.local 46 | !.env 47 | 48 | # AI 49 | CLAUDE.md 50 | .agent/ 51 | .claude/ 52 | .gemini/ 53 | -------------------------------------------------------------------------------- /packages/live-tiles/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "composite": true, 7 | "outDir": "./dist", 8 | "tsBuildInfoFile": ".tsbuildinfo", 9 | "typeRoots": ["./node_modules/@types", "./src/@types"], 10 | "types": [], 11 | "paths": { 12 | "*": ["*", "./src/@types/*"], 13 | 14 | "~/*": ["src/*"], 15 | "~/utils/*": ["src/utils/*"], 16 | "~/types": ["src/@types/index.ts"] 17 | } 18 | }, 19 | "exclude": ["dist"], 20 | "include": ["src", "src/@types"] 21 | } 22 | -------------------------------------------------------------------------------- /apps/launcher/src/utils/cn.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from '@jest/globals'; 2 | 3 | import { cn } from '~/utils/cn'; 4 | 5 | describe('utils', () => { 6 | describe('cn', () => { 7 | it('should return `undefined` when given no class name', () => { 8 | const result = cn(); 9 | expect(result).toBeUndefined(); 10 | }); 11 | 12 | it('should merge tailwind classes', () => { 13 | const result = cn('text-red-500', 'text-green-500'); 14 | expect(result).toBe('text-green-500'); 15 | }); 16 | 17 | it('should keep unknown classes', () => { 18 | const result = cn('text-red-500', 'unknown-class'); 19 | expect(result).toBe('text-red-500 unknown-class'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/launcher/src/components/text.tsx: -------------------------------------------------------------------------------- 1 | import { Text as RNText, type TextProps as RNTextProps } from 'react-native'; 2 | import { cn } from '~/utils/cn'; 3 | 4 | export type TextProps = RNTextProps & { 5 | type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; 6 | }; 7 | 8 | export function Text({ className, type = 'default', ...rest }: TextProps) { 9 | return ( 10 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | docs: 9 | name: 📖 Docs 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: 🕶️ Checkout repository 13 | uses: actions/checkout@v6 14 | 15 | - name: 🚀 Bootstrap 16 | uses: ./.github/common/bootstrap 17 | 18 | - name: 🔨 Build docs 19 | run: pnpm run docs 20 | 21 | - uses: peaceiris/actions-gh-pages@v4 22 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 23 | with: 24 | github_token: ${{ secrets.GITHUB_TOKEN }} 25 | publish_dir: ./docs 26 | enable_jekyll: false 27 | commit_message: ${{ github.event.head_commit.message }} 28 | force_orphan: true 29 | -------------------------------------------------------------------------------- /apps/launcher/metro.config.cjs: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig } = require('expo/metro-config'); 2 | const { withNativeWind } = require('nativewind/metro'); 3 | 4 | module.exports = (() => { 5 | /** @type {import('expo/metro-config').MetroConfig} */ 6 | const config = getDefaultConfig(__dirname); // eslint-disable-line no-undef 7 | const { transformer, resolver } = config; 8 | 9 | config.transformer = { 10 | ...transformer, 11 | babelTransformerPath: require.resolve('react-native-svg-transformer/expo') 12 | }; 13 | config.resolver = { 14 | ...resolver, 15 | assetExts: [...resolver.assetExts.filter((ext) => ext !== 'svg'), 'wasm'], 16 | sourceExts: [...resolver.sourceExts, 'svg', 'sql'], 17 | unstable_enablePackageExports: false 18 | }; 19 | 20 | return withNativeWind(config, { 21 | input: './src/styles/global.css', 22 | configPath: './tailwind.config.ts' 23 | }); 24 | })(); 25 | -------------------------------------------------------------------------------- /apps/launcher/src/utils/i18n.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | 3 | import { getLocales } from 'expo-localization'; 4 | import i18n from 'i18next'; 5 | import { initReactI18next } from 'react-i18next'; 6 | import english from '~/i18n/en.json'; 7 | 8 | export type i18nKey = keyof typeof english; 9 | 10 | const resources = { 11 | en: { translation: english } 12 | }; 13 | 14 | const initI18n = async () => { 15 | // eslint-disable-next-line import/no-named-as-default-member 16 | i18n.use(initReactI18next).init({ 17 | compatibilityJSON: 'v4', 18 | resources, 19 | lng: getLocales()[0]?.languageCode!, 20 | fallbackLng: 'en', 21 | supportedLngs: Object.keys(resources), 22 | cleanCode: true, 23 | ns: ['translation'], 24 | defaultNS: 'translation', 25 | interpolation: { 26 | escapeValue: false 27 | } 28 | }); 29 | }; 30 | 31 | initI18n(); 32 | 33 | export default i18n; 34 | -------------------------------------------------------------------------------- /apps/launcher/src/hooks/use-color-scheme.tsx: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import { useCallback, useEffect, useState } from 'react'; 3 | import { Appearance } from 'react-native'; 4 | 5 | export function useColorScheme(delay = 250) { 6 | const [colorScheme, setColorScheme] = useState(Appearance.getColorScheme()); 7 | 8 | // eslint-disable-next-line react-hooks/exhaustive-deps 9 | const onColorSchemeChange = useCallback( 10 | _.throttle( 11 | (theme: any) => { 12 | setColorScheme(theme.colorScheme); 13 | }, 14 | delay, 15 | { leading: false } 16 | ), 17 | [] 18 | ); 19 | 20 | useEffect(() => { 21 | const subscription = Appearance.addChangeListener(onColorSchemeChange); 22 | return () => { 23 | onColorSchemeChange.cancel(); 24 | subscription.remove(); 25 | }; 26 | }, [onColorSchemeChange]); 27 | 28 | return { colorScheme }; 29 | } 30 | -------------------------------------------------------------------------------- /packages/live-tiles/tsup.config.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { fileURLToPath } from 'node:url'; 3 | import * as glob from 'glob'; 4 | 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 6 | 7 | const entries = glob.sync(['./src/**/*.ts{,x}'], { ignore: ['./src/**/*.test.*', './src/**/*.d.ts'] }); 8 | console.log(entries, __dirname, path.join(__dirname, './tsconfig.json')); 9 | 10 | /** 11 | * @type {import("tsup").Options} 12 | */ 13 | export default { 14 | bundle: false, 15 | clean: true, 16 | external: ['react'], 17 | dts: { 18 | compilerOptions: { 19 | composite: false // Work-around for dts failing when we're using `composite` mode. 20 | }, 21 | entry: entries, 22 | resolve: true 23 | }, 24 | entry: entries, 25 | format: ['esm'], 26 | keepNames: true, 27 | platform: 'neutral', 28 | skipNodeModulesBundle: true, 29 | sourcemap: 'inline', 30 | splitting: false, 31 | treeshake: true, 32 | tsconfig: './tsconfig.json' 33 | }; 34 | -------------------------------------------------------------------------------- /apps/launcher/src/app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import '~/styles/global.css'; 2 | 3 | import { Stack } from 'expo-router'; 4 | import { StatusBar } from 'expo-status-bar'; 5 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 6 | import { useColorScheme } from '~/hooks/use-color-scheme'; 7 | import { useTheme } from '~/hooks/use-theme'; 8 | import { ProvidersRegistry } from '~/providers/registry'; 9 | import { TAILWIND_THEME } from '~/styles/theme'; 10 | 11 | export default function Layout() { 12 | const { theme } = useTheme(); 13 | 14 | const { colorScheme } = useColorScheme(); 15 | const activeTheme = colorScheme === 'dark' ? 'dark' : 'light'; 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /apps/launcher/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "composite": true, 7 | "jsx": "react-native", 8 | "outDir": "./dist", 9 | "tsBuildInfoFile": ".tsbuildinfo", 10 | "typeRoots": ["./src/@types"], 11 | "types": ["nativewind/types"], 12 | "paths": { 13 | "*": ["*", "./src/@types/*"], 14 | 15 | "~/*": ["src/*"], 16 | "~/assets/*": ["./assets/*"], 17 | "~/utils/*": ["src/utils/*"], 18 | "~/i18n/*": ["./i18n/*"], 19 | "~/providers/*": ["src/providers/*"], 20 | "~/pages/*": ["src/app/(root)/*"], 21 | "~/hooks/*": ["src/hooks/*"], 22 | "~/styles/*": ["src/styles/*"] 23 | } 24 | }, 25 | "exclude": ["dist"], 26 | "include": [ 27 | "./index.ts", 28 | "src", 29 | "src/@types", 30 | "i18n/*.json", 31 | ".expo/types/**/*.ts", 32 | "expo-env.d.ts", 33 | "nativewind-env.d.ts" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /packages/live-tiles/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'jest'; 2 | 3 | const jestConfig: Config = { 4 | globals: { 5 | __DEV__: true 6 | }, 7 | rootDir: '.', 8 | collectCoverage: true, 9 | coverageProvider: 'v8', 10 | coverageReporters: ['json'], 11 | json: true, 12 | collectCoverageFrom: ['./src/**/*.*', '!**/node_modules/**'], 13 | testEnvironment: 'node', 14 | transform: { 15 | '^.+\\.tsx?$': [ 16 | 'ts-jest', 17 | { 18 | tsconfig: './tsconfig.test.json' 19 | } 20 | ], 21 | '^.+\\.jsx?$': ['babel-jest', { configFile: './babel.config.cjs' }] 22 | }, 23 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 24 | moduleNameMapper: { 25 | '^~/(.*)$': '/src/$1' 26 | }, 27 | testPathIgnorePatterns: ['dist/', 'node_modules/'], 28 | transformIgnorePatterns: ['/node_modules/(?!(@react-native|react-native|react-native-gesture-handler)/).*/'], 29 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[mc]?[jt]sx?$', 30 | clearMocks: true, 31 | useStderr: true 32 | }; 33 | 34 | export default jestConfig; 35 | -------------------------------------------------------------------------------- /packages/live-tiles/src/tiles/tile-density.ts: -------------------------------------------------------------------------------- 1 | import { Size } from '../utils/size'; 2 | 3 | export class TileDensity { 4 | public static desktop(): TileDensity { 5 | return new TileDensity(new Size(48, 48), new Size(100, 100), new Size(204, 100), new Size(204, 204)); 6 | } 7 | 8 | public static tablet(): TileDensity { 9 | return new TileDensity(new Size(60, 60), new Size(125, 125), new Size(255, 125), new Size(255, 255)); 10 | } 11 | 12 | public static mobile(customDensity: number): TileDensity { 13 | return new TileDensity( 14 | new Size(48 * customDensity, 48 * customDensity), 15 | new Size(100 * customDensity, 100 * customDensity), 16 | new Size(204 * customDensity, 100 * customDensity), 17 | new Size(204 * customDensity, 204 * customDensity) 18 | ); 19 | } 20 | 21 | public readonly small: Size; 22 | public readonly medium: Size; 23 | public readonly wide: Size; 24 | public readonly large: Size; 25 | 26 | private constructor(small: Size, medium: Size, wide: Size, large: Size) { 27 | this.small = small; 28 | this.medium = medium; 29 | this.wide = wide; 30 | this.large = large; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/live-tiles/src/utils/size.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from '@jest/globals'; 2 | 3 | import { Size } from './size'; 4 | 5 | describe('Size', () => { 6 | it('should retrieve values correctly', () => { 7 | const size = new Size(2.75, 0); 8 | 9 | expect(size.getWidth()).toBe(2.75); 10 | expect(size.getHeight()).toBe(0); 11 | }); 12 | 13 | it('should convert an object to a size properly', () => { 14 | const size = Size.fromObject({ width: 250, height: 100 }); 15 | 16 | expect(size.getWidth()).toBe(250); 17 | expect(size.getHeight()).toBe(100); 18 | }); 19 | 20 | it('should get a zero width/height size from Size.ZERO', () => { 21 | const size = Size.ZERO; 22 | 23 | expect(size.getWidth()).toBe(0); 24 | expect(size.getHeight()).toBe(0); 25 | }); 26 | 27 | it('should set and get the width coordinate correctly', () => { 28 | const size = new Size(); 29 | 30 | size.setWidth(10); 31 | expect(size.getWidth()).toBe(10); 32 | }); 33 | 34 | it('should set and get the height coordinate correctly', () => { 35 | const size = new Size(); 36 | 37 | size.setHeight(10); 38 | expect(size.getHeight()).toBe(10); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /apps/launcher/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ['./index.ts', './src/app/index.tsx', './src/**/*.{js,jsx,ts,tsx}'], 5 | presets: [require('nativewind/preset')], 6 | theme: { 7 | extend: { 8 | animation: {}, 9 | keyframes: {}, 10 | borderWidth: { 11 | 3: '3px', 12 | 5: '5px', 13 | 6: '6px', 14 | 7: '7px' 15 | }, 16 | height: { 17 | 17: '4.25rem', 18 | 18: '4.5rem' 19 | }, 20 | colors: { 21 | 'primary-base': 'var(--primary-base)', 22 | 'text-base': 'var(--text-base)', 23 | 'background-base': 'var(--background-base)', 24 | 'border-base': 'var(--border-base)', 25 | 'card-base': 'var(--card-base)', 26 | 'tint-base': 'var(--tint-base)', 27 | 'icon-base': 'var(--icon-base)' 28 | }, 29 | stroke: { 30 | 3: '3', 31 | 4: '4' 32 | }, 33 | screens: { 34 | sm: '240px', 35 | md: '420px', 36 | lg: '1024px', 37 | xl: '1280px' 38 | } 39 | } 40 | }, 41 | plugins: [] 42 | }; 43 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "ui": "stream", 4 | "globalDependencies": [ 5 | "**/.env.*local", 6 | "tsconfig.json", 7 | "**/tsconfig.json", 8 | "package.json", 9 | "**/package.json", 10 | ".env", 11 | "**/.env", 12 | "*.env", 13 | "**/*.env" 14 | ], 15 | "tasks": { 16 | "test": { 17 | "cache": false 18 | }, 19 | "start": { 20 | "cache": false, 21 | "persistent": true, 22 | "inputs": ["$TURBO_DEFAULT$", ".env.development.local", ".env.local", ".env.development", ".env"] 23 | }, 24 | "build": { 25 | "cache": true, 26 | "outputLogs": "new-only", 27 | "dependsOn": ["^build"], 28 | "outputs": [".tsup/**", ".expo/**", "!.expo/cache/**", "dist/**", "ios/**", "android/**"], 29 | "inputs": [ 30 | "$TURBO_DEFAULT$", 31 | "!README.md", 32 | ".env.production.local", 33 | ".env.local", 34 | ".env.production", 35 | ".env" 36 | ] 37 | }, 38 | "typecheck": { 39 | "cache": false 40 | }, 41 | "docs": { 42 | "cache": true, 43 | "outputs": ["docs/**"], 44 | "dependsOn": ["^docs"], 45 | "inputs": ["$TURBO_DEFAULT$", "!README.md", "typedoc.json", "**/typedoc.json"] 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/live-tiles/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@metro-ui/live-tiles", 3 | "version": "0.0.0", 4 | "private": false, 5 | "types": "./dist/index.d.ts", 6 | "main": "./dist/index.js", 7 | "type": "module", 8 | "packageManager": "pnpm@10.26.1", 9 | "engines": { 10 | "node": ">=24.0.0" 11 | }, 12 | "files": [ 13 | "dist", 14 | "README.md" 15 | ], 16 | "exports": { 17 | ".": { 18 | "import": "./dist/index.js", 19 | "types": "./dist/index.d.ts" 20 | }, 21 | "./*": { 22 | "import": "./dist/*.js", 23 | "types": "./dist/*.d.ts" 24 | }, 25 | "./**/*": { 26 | "import": "./dist/**/*.js", 27 | "types": "./dist/**/*.d.ts" 28 | } 29 | }, 30 | "scripts": { 31 | "test": "jest --passWithNoTests", 32 | "test:watch": "jest --watchAll", 33 | "build": "tsup", 34 | "typecheck": "tsc --noEmit", 35 | "lint": "biome lint .", 36 | "format": "biome format --write .", 37 | "check": "biome check .", 38 | "check:fix": "biome check --write ." 39 | }, 40 | "dependencies": {}, 41 | "devDependencies": { 42 | "@jest/globals": "30.2.0", 43 | "@types/jest": "29.5.14", 44 | "@types/node": "24.10.4", 45 | "dotenv-cli": "11.0.0", 46 | "glob": "13.0.0", 47 | "jest": "29.7.0", 48 | "ts-jest": "29.4.6", 49 | "tsup": "8.5.1", 50 | "typescript": "5.9.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://typedoc.org/schema.json", 3 | "name": "AdaptiveShell", 4 | "categorizeByGroup": false, 5 | "entryPoints": ["apps/*", "packages/*"], 6 | "entryPointStrategy": "packages", 7 | "tsconfig": "./tsconfig.json", 8 | "plugin": [ 9 | "typedoc-material-theme", 10 | "typedoc-plugin-extras", 11 | "typedoc-plugin-merge-modules", 12 | "typedoc-plugin-rename-defaults", 13 | "typedoc-plugin-dt-links", 14 | "typedoc-plugin-missing-exports", 15 | "typedoc-plugin-inline-sources" 16 | ], 17 | "themeColor": "#3C93F0", 18 | "favicon": ".github/img/favicon.png", 19 | "basePath": "./", 20 | "mergeModulesRenameDefaults": true, 21 | "mergeModulesMergeMode": "module", 22 | "exclude": [ 23 | "**/node_modules/**", 24 | "**/dist/**", 25 | "**/build/**", 26 | "**/coverage/**", 27 | "**/test/**", 28 | "**/__tests__/**", 29 | "**/*.spec.ts", 30 | "**/*.test.ts" 31 | ], 32 | "includeVersion": true, 33 | "out": "./docs", 34 | "readme": "./README.md", 35 | "projectDocuments": ["./CONTRIBUTING.md", "./LICENSE"], 36 | "excludeExternals": true, 37 | "excludeInternal": true, 38 | "excludePrivate": true, 39 | "excludeProtected": true, 40 | "externalPattern": ["**/node_modules/**", ".*+(apps|packages)(/[^/]+)/.*"], 41 | "sourceLinkExternal": true, 42 | "footerLastModified": true, 43 | "useTsLinkResolution": true, 44 | "githubPages": true 45 | } 46 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.locale": "en", 4 | "git.autofetch": "all", 5 | "git.countBadge": "tracked", 6 | "git.fetchOnPull": true, 7 | "git.inputValidationSubjectLength": 72, 8 | "git.inputValidationLength": 72, 9 | "git.inputValidation": true, 10 | "git.showPushSuccessNotification": true, 11 | "github.copilot.enable": { 12 | "plaintext": true, 13 | "markdown": true 14 | }, 15 | "github.copilot.chat.localeOverride": "en", 16 | "editor.codeActionsOnSave": { 17 | "source.fixAll": "explicit", 18 | "source.organizeImports": "explicit", 19 | "source.sortMembers": "explicit" 20 | }, 21 | "files.autoSave": "off", 22 | "files.trimTrailingWhitespace": true, 23 | "files.eol": "\n", 24 | "files.trimFinalNewlines": false, 25 | "[plaintext]": { 26 | "editor.formatOnSave": false 27 | }, 28 | "[git-commit]": { 29 | "editor.rulers": [72], 30 | "editor.wordWrap": "off", 31 | "workbench.editor.restoreViewState": false 32 | }, 33 | "cSpell.words": [ 34 | "bitcode", 35 | "codegen", 36 | "commitlint", 37 | "nativewind", 38 | "nvmrc", 39 | "Pressable", 40 | "startscreen", 41 | "Subviews", 42 | "tsup" 43 | ], 44 | "githubCodeOwners.format.enabled": true, 45 | "typescript.preferences.importModuleSpecifier": "non-relative", 46 | "typescript.experimental.expandableHover": true, 47 | "jsdoc-generator.includeExport": false, 48 | "jsdoc-generator.emptyLineAfterHeader": true 49 | } 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a detailed report to help us track and fix issues 4 | title: 'Bug: ' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ### 🐛 Bug Description 10 | 11 | A clear and concise description of what the bug is. What happened? What went wrong? 12 | 13 | ### 🔄 Steps to Reproduce 14 | 15 | 1. 16 | 2. 17 | 3. 18 | 19 | ### ✅ Expected Behavior 20 | 21 | Describe what you expected to happen when following the steps above. 22 | 23 | ### ❌ Actual Behavior 24 | 25 | Describe what actually happened when following the steps above. 26 | 27 | ### 📱 Environment 28 | 29 | - OS: [e.g. Windows 11, macOS 13.0, Ubuntu 22.04] 30 | - Architecture: [e.g. x64, ARM64] 31 | - AdaptiveShell Version: [e.g. 1.0.0, #a515c34] 32 | - Running Applications: [Applications you were using AdaptiveShell with] 33 | 34 | ### 📸 Screenshots / Videos 35 | 36 | If applicable, add screenshots or videos to help explain your problem. 37 | 38 | 39 | 40 | ### 📊 Logs 41 | 42 |
43 | Relevant log output 44 | 45 | ``` 46 | Paste any relevant log output here 47 | ``` 48 | 49 |
50 | 51 | ### ℹ️ Additional Context 52 | 53 | - Have you tried updating to the latest version? 54 | - Does this happen consistently or intermittently? 55 | - Any other relevant information that might help us identify and fix the issue. 56 | 57 | ### ✔️ Checklist 58 | 59 | - [ ] I have searched for existing issues before creating this one 60 | - [ ] I have tested with the latest version of AdaptiveShell 61 | - [ ] I have provided all the information requested above 62 | - [ ] I have included relevant logs or error messages 63 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "expo/tsconfig.base", 4 | "compilerOptions": { 5 | "allowImportingTsExtensions": true, 6 | "allowJs": false, 7 | "allowSyntheticDefaultImports": true, 8 | "baseUrl": "./", 9 | "composite": true, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "downlevelIteration": false, 13 | "esModuleInterop": true, 14 | "experimentalDecorators": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "incremental": true, 17 | "isolatedModules": true, 18 | "lib": ["DOM", "ESNext"], 19 | "module": "ESNext", 20 | "moduleDetection": "force", 21 | "moduleResolution": "Bundler", 22 | "noFallthroughCasesInSwitch": true, 23 | "noImplicitAny": true, 24 | "noUncheckedIndexedAccess": true, 25 | "noUnusedLocals": true, 26 | "noUnusedParameters": true, 27 | "plugins": [], 28 | "preserveConstEnums": true, 29 | "removeComments": false, 30 | "resolveJsonModule": true, 31 | "skipLibCheck": true, 32 | "sourceMap": true, 33 | "strict": true, 34 | "strictBindCallApply": true, 35 | "strictFunctionTypes": true, 36 | "strictNullChecks": true, 37 | "strictPropertyInitialization": true, 38 | "tsBuildInfoFile": ".tsbuildinfo", 39 | "typeRoots": ["node_modules/@types", "./src/@types", "**/src/@types"], 40 | "types": ["node"], 41 | "useDefineForClassFields": true 42 | }, 43 | "exclude": ["dist", "tailwind.config.ts", "build", "node_modules", "**/*.test.ts", "**/*.test.tsx"] 44 | } 45 | -------------------------------------------------------------------------------- /apps/launcher/src/providers/registry.tsx: -------------------------------------------------------------------------------- 1 | import type { Theme } from '@react-navigation/native'; 2 | import { ThemeProvider } from '@react-navigation/native'; 3 | import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'; 4 | import type { ReactNode } from 'react'; 5 | import { I18nextProvider } from 'react-i18next'; 6 | import { KeyboardProvider } from 'react-native-keyboard-controller'; 7 | import { initialWindowMetrics, SafeAreaProvider } from 'react-native-safe-area-context'; 8 | import i18next from '~/utils/i18n'; 9 | import SplashProvider from './splash-provider'; 10 | 11 | const queryClient = new QueryClient({ 12 | queryCache: new QueryCache({ 13 | onError: (error) => { 14 | console.error('Error in query:', error); 15 | // Global error handling here (e.g., Toast notification) 16 | } 17 | }), 18 | mutationCache: new MutationCache({ 19 | onError: (error) => { 20 | console.error('Error in mutation:', error, (error as any).session); 21 | // Global error handling for mutations 22 | } 23 | }) 24 | }); 25 | 26 | export const ProvidersRegistry = ({ theme, children }: { theme: Theme; children: ReactNode }) => ( 27 | 28 | 29 | 30 | 31 | 32 | {children} 33 | 34 | 35 | 36 | 37 | 38 | ); 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Logo](https://raw.githubusercontent.com/filiphsps/AdaptiveShell/refs/heads/legacy/xaml-islands/Shell.Packaging/Images/Square44x44Logo.targetsize-24_altform-unplated.png) Adaptive Shell 2 | 3 | An adaptive and modern shell replacement for mobile and tablet computing devices running Windows 11 (with Windows 10 backwards-compatibility). 4 | 5 | > [!WARNING] 6 | > We just started the process of migrating the codebase to TypeScript and React Native, you can still find the legacy XAML Islands version in the [legacy branches](https://github.com/filiphsps/AdaptiveShell/tree/legacy/xaml-islands). 7 | 8 | ## User interface 9 | 10 | | ![](https://raw.githubusercontent.com/filiphsps/AdaptiveShell/refs/heads/legacy/xaml-islands/.github/img/start-dc70ce9.png) | ![](https://raw.githubusercontent.com/filiphsps/AdaptiveShell/refs/heads/legacy/xaml-islands/.github/img/apps-dc70ce9.png) | ![](https://raw.githubusercontent.com/filiphsps/AdaptiveShell/refs/heads/legacy/xaml-islands/.github/img/settings-dc70ce9.png) | 11 | | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | 12 | | Start screen | All apps | Settings | 13 | 14 | _Screenshot on Surface Pro 2 [dc70ce9](https://github.com/w10m-research/AdaptiveShell/commit/dc70ce9cec578cf9b6a7338596697e6106613123)_ 15 | -------------------------------------------------------------------------------- /apps/launcher/src/providers/splash-provider.tsx: -------------------------------------------------------------------------------- 1 | import { useFonts } from 'expo-font'; 2 | import * as SplashScreen from 'expo-splash-screen'; 3 | import type { ReactNode } from 'react'; 4 | import { useEffect, useState } from 'react'; 5 | import { Text, View } from 'react-native'; 6 | import { SafeAreaView } from 'react-native-safe-area-context'; 7 | import { useColorScheme } from '~/hooks/use-color-scheme'; 8 | import { cn } from '~/utils/cn'; 9 | 10 | SplashScreen.preventAutoHideAsync(); 11 | SplashScreen.setOptions({ 12 | duration: 500, 13 | fade: true 14 | }); 15 | 16 | export default function SplashProvider({ children }: { children: ReactNode }) { 17 | const { colorScheme } = useColorScheme(); 18 | const [fontsLoaded] = useFonts({ 19 | SpaceMono: require('../../assets/fonts/SpaceMono-Regular.ttf') 20 | }); 21 | 22 | const [showSplash, setShowSplash] = useState(true); 23 | 24 | useEffect(() => { 25 | if (!colorScheme) return; 26 | SplashScreen.hideAsync(); 27 | 28 | if (!fontsLoaded || !showSplash) return; 29 | setShowSplash(false); 30 | }, [colorScheme, fontsLoaded, showSplash]); 31 | 32 | let loadingState = ''; 33 | if (!fontsLoaded) { 34 | loadingState = 'fonts'; 35 | } 36 | 37 | return ( 38 | <> 39 | 45 | 46 | {loadingState ? ` ${loadingState}` : ''}... 47 | 48 | 49 | 50 | {children} 51 | 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /packages/live-tiles/src/utils/size.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A Size, essentially a 2D Vector. 3 | */ 4 | export class Size { 5 | /** 6 | * Returns a Size with 0 as it's width and height. 7 | */ 8 | public static get ZERO(): Size { 9 | return new Size(0, 0); 10 | } 11 | 12 | /** 13 | * Create a new `Size` instance. 14 | * @param {number} width - The width. 15 | * @param {number} height - The height. 16 | * @example 17 | * ```typescript 18 | * const size = new Size(10, 20); 19 | * ``` 20 | */ 21 | public constructor( 22 | protected width: number = 0, 23 | protected height: number = 0 24 | ) {} 25 | 26 | /** 27 | * Creates a new Size instance from an object with width and height properties. 28 | * @param obj - The object containing width and height properties. 29 | * @returns {Size} A new Size instance. 30 | */ 31 | public static fromObject({ width, height }: { width: number; height: number }): Size { 32 | return new Size(width, height); 33 | } 34 | 35 | /** 36 | * Set the width. 37 | * @param {number} width - The width. 38 | * @example 39 | * ```typescript 40 | * size.setX(10); 41 | * ``` 42 | */ 43 | public setWidth(width: number): void { 44 | this.width = width; 45 | } 46 | 47 | /** 48 | * Set the height. 49 | * @param {number} height - The height. 50 | * @example 51 | * ```typescript 52 | * size.setZ(10); 53 | * ``` 54 | */ 55 | public setHeight(height: number): void { 56 | this.height = height; 57 | } 58 | 59 | /** 60 | * Get the width. 61 | * @returns {number} The width. 62 | */ 63 | public getWidth(): number { 64 | return this.width; 65 | } 66 | 67 | /** 68 | * Get the height. 69 | * @returns {number} The height. 70 | */ 71 | public getHeight(): number { 72 | return this.height; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /apps/launcher/src/styles/theme.ts: -------------------------------------------------------------------------------- 1 | import type { Theme } from '@react-navigation/native'; 2 | import { DarkTheme, DefaultTheme } from '@react-navigation/native'; 3 | import { vars } from 'nativewind'; 4 | 5 | export type ExtendedTheme = Theme & { 6 | colors: Theme['colors'] & { 7 | icon: string; 8 | tint: string; 9 | }; 10 | }; 11 | 12 | export const LIGHT_THEME: ExtendedTheme = { 13 | dark: false, 14 | colors: { 15 | primary: '#11181C', 16 | text: '#11181C', 17 | background: '#fcfcfc', 18 | card: 'rgb(255, 255, 255)', 19 | tint: '#0a7ea4', 20 | icon: 'rgb(60, 60, 60)', 21 | border: 'rgb(216, 216, 216)', 22 | notification: 'rgb(255, 59, 48)' 23 | }, 24 | fonts: { 25 | ...(DefaultTheme.fonts as Theme['fonts']) 26 | } 27 | }; 28 | export const DARK_THEME: ExtendedTheme = { 29 | dark: true, 30 | colors: { 31 | primary: '#ECEDEE', 32 | text: '#ECEDEE', 33 | background: '#000000', 34 | card: 'rgb(18, 18, 18)', 35 | tint: '#ffffff', 36 | icon: 'rgb(130, 130, 130)', 37 | border: 'rgb(39, 39, 41)', 38 | notification: 'rgb(255, 69, 58)' 39 | }, 40 | fonts: { 41 | ...(DarkTheme.fonts as Theme['fonts']) 42 | } 43 | }; 44 | 45 | export const TAILWIND_THEME = { 46 | light: vars({ 47 | '--primary-base': LIGHT_THEME.colors.primary, 48 | '--text-base': LIGHT_THEME.colors.text, 49 | '--background-base': LIGHT_THEME.colors.background, 50 | '--border-base': LIGHT_THEME.colors.border, 51 | '--card-base': LIGHT_THEME.colors.card, 52 | '--tint-base': LIGHT_THEME.colors.tint, 53 | '--icon-base': LIGHT_THEME.colors.tint 54 | }), 55 | dark: vars({ 56 | '--primary-base': DARK_THEME.colors.primary, 57 | '--text-base': DARK_THEME.colors.text, 58 | '--background-base': DARK_THEME.colors.background, 59 | '--border-base': DARK_THEME.colors.border, 60 | '--card-base': DARK_THEME.colors.card, 61 | '--tint-base': DARK_THEME.colors.tint, 62 | '--icon-base': DARK_THEME.colors.icon 63 | }) 64 | }; 65 | -------------------------------------------------------------------------------- /apps/launcher/app.config.ts: -------------------------------------------------------------------------------- 1 | import 'ts-node/register'; 2 | 3 | import type { ConfigContext, ExpoConfig } from 'expo/config'; 4 | 5 | export default ({ config }: ConfigContext): ExpoConfig => ({ 6 | ...config, 7 | name: 'metro', 8 | slug: 'metro', 9 | userInterfaceStyle: 'automatic', 10 | version: '0.1.0', 11 | runtimeVersion: '0.1.0', 12 | orientation: 'portrait', 13 | icon: './assets/images/icon.png', 14 | assetBundlePatterns: ['**/*', 'assets/**/*'], 15 | newArchEnabled: true, 16 | jsEngine: 'hermes', 17 | splash: { 18 | image: './assets/images/splash-icon.png', 19 | resizeMode: 'contain', 20 | backgroundColor: '#ffffff' 21 | }, 22 | ios: { 23 | bitcode: true, 24 | supportsTablet: true, 25 | bundleIdentifier: 'com.filiphsandstrom.metro', 26 | userInterfaceStyle: 'automatic' 27 | }, 28 | android: { 29 | softwareKeyboardLayoutMode: 'pan', 30 | adaptiveIcon: { 31 | foregroundImage: './assets/images/adaptive-icon.png', 32 | backgroundColor: '#ffffff' 33 | }, 34 | package: 'com.filiphsandstrom.metro', 35 | userInterfaceStyle: 'automatic', 36 | edgeToEdgeEnabled: true, 37 | permissions: [] 38 | }, 39 | web: { 40 | bundler: 'metro', 41 | favicon: './assets/images/favicon.png' 42 | }, 43 | plugins: [ 44 | [ 45 | 'expo-asset', 46 | { 47 | assets: ['./assets'] 48 | } 49 | ], 50 | [ 51 | 'expo-router', 52 | { 53 | root: './src/app' 54 | } 55 | ], 56 | [ 57 | 'expo-dev-client', 58 | { 59 | launchMode: 'most-recent' 60 | } 61 | ], 62 | [ 63 | 'expo-font', 64 | { 65 | fonts: ['./assets/fonts/SpaceMono-Regular.ttf'], 66 | android: { 67 | fonts: [ 68 | { 69 | fontFamily: 'SpaceMono', 70 | fontDefinitions: [ 71 | { 72 | path: './assets/fonts/SpaceMono-Regular.ttf', 73 | weight: 400 74 | } 75 | ] 76 | } 77 | ] 78 | } 79 | } 80 | ] 81 | ], 82 | experiments: { 83 | reactCompiler: true, 84 | tsconfigPaths: true, 85 | typedRoutes: true 86 | }, 87 | extra: { 88 | router: { 89 | origin: false, 90 | root: './src/app/_layout.tsx' 91 | } 92 | } 93 | }); 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@metro-ui/metro", 3 | "private": true, 4 | "packageManager": "pnpm@10.26.1", 5 | "engines": { 6 | "node": ">=24.0.0" 7 | }, 8 | "scripts": { 9 | "prepare": "husky", 10 | "precommit": "lint-staged", 11 | "create": "turbo gen", 12 | "test": "dotenv -c -- turbo run test && pnpm run coverage:merge", 13 | "start": "dotenv -c -- expo start", 14 | "build": "dotenv -c -- turbo run build --env-mode=loose", 15 | "build:packages": "dotenv -c -- turbo run build --filter=./packages/* --env-mode=loose", 16 | "lint": "biome lint .", 17 | "typecheck": "turbo run typecheck --", 18 | "format": "biome format --write .", 19 | "format:check": "biome format .", 20 | "check": "biome check .", 21 | "check:fix": "biome check --write --unsafe .", 22 | "coverage:merge": "istanbul-merge --out coverage/coverage-final.json ./packages/**/coverage/coverage-final.json ./apps/**/coverage/coverage-final.json && nyc report --reporter=html --reporter=text --reporter=lcov --reporter=clover --report-dir=coverage --temp-dir=coverage", 23 | "version": "changeset version", 24 | "version:unstable": "changeset version --snapshot unstable", 25 | "release": "changeset publish", 26 | "release:unstable": "changeset publish --snapshot --no-git-tag --tag unstable", 27 | "changeset": "changeset", 28 | "changeset:conventional": "changeset-conventional", 29 | "docs": "typedoc" 30 | }, 31 | "contributors": [ 32 | { 33 | "name": "Filiph Sandström", 34 | "email": "filfat@hotmail.se", 35 | "url": "https://github.com/filiphsps" 36 | } 37 | ], 38 | "workspaces": [ 39 | "apps/**", 40 | "packages/**" 41 | ], 42 | "lint-staged": { 43 | "*.{ts,tsx,js,jsx,json,jsonc}": [ 44 | "biome check --write --no-errors-on-unmatched" 45 | ] 46 | }, 47 | "devDependencies": { 48 | "@biomejs/biome": "2.3.10", 49 | "@changesets/changelog-github": "0.5.2", 50 | "@changesets/cli": "2.29.8", 51 | "@changesets/types": "6.1.0", 52 | "@commitlint/cli": "20.2.0", 53 | "@commitlint/config-conventional": "20.2.0", 54 | "@types/node": "24.10.4", 55 | "changeset-conventional-commits": "0.2.5", 56 | "dotenv-cli": "11.0.0", 57 | "husky": "9.1.7", 58 | "istanbul-merge": "2.0.0", 59 | "lint-staged": "16.2.7", 60 | "nyc": "17.1.0", 61 | "turbo": "2.7.1", 62 | "turbo-ignore": "2.7.1", 63 | "typedoc": "0.28.15", 64 | "typedoc-material-theme": "1.4.1", 65 | "typedoc-plugin-dt-links": "2.0.34", 66 | "typedoc-plugin-extras": "4.0.1", 67 | "typedoc-plugin-inline-sources": "1.3.0", 68 | "typedoc-plugin-merge-modules": "7.0.0", 69 | "typedoc-plugin-missing-exports": "4.1.2", 70 | "typedoc-plugin-rename-defaults": "0.7.3", 71 | "typescript": "5.9.3" 72 | }, 73 | "pnpm": { 74 | "overrides": { 75 | "typescript": "$typescript" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to AdaptiveShell 2 | 3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: 4 | 5 | #### Table Of Contents 6 | 7 | [How Can I Contribute?](#how-can-i-contribute) 8 | 9 | [Reporting Bugs](#reporting-bugs) 10 | 11 | - [Before Submitting A Bug Report](#before-submitting-a-bug-report) 12 | - [How Do I Submit A (Good) Bug Report?](#how-do-i-submit-a-good-bug-report) 13 | 14 | [Useful Links](#useful-links) 15 | 16 | --- 17 | 18 | ## How Can I Contribute? 19 | 20 | TODO 21 | 22 | --- 23 | 24 | ## Reporting Bugs 25 | 26 | This guide helps you submit effective bug reports for our projects. Clear, detailed reports help maintainers and the community understand, reproduce, and resolve issues efficiently. 27 | 28 | # Before Submitting A Bug Report 29 | 30 | Before creating a new report, please review [this list](#before-submitting-a-bug-report) to avoid duplicate submissions. When reporting bugs, [include comprehensive details](#how-do-i-submit-a-good-bug-report) and use [our template](https://github.com/filiphsps/AdaptiveShell/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) to ensure all necessary information is provided. 31 | 32 | > [!NOTE] 33 | > If you find a **closed** issue similar to your current problem, please create a new issue and reference the original one in your description as that will help us find the regression way faster. 34 | 35 | ### How Do I Submit A (Good) Bug Report? 36 | 37 | Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue in our repository and provide detailed information using [our template](https://github.com/filiphsps/AdaptiveShell/blob/master/.github/ISSUE_TEMPLATE/bug_report.md). 38 | 39 | To help us resolve issues quickly, please include: 40 | 41 | 1. **Title**: Use a clear, descriptive title that identifies the problem. 42 | 2. **Steps to Reproduce**: 43 | - Detail exactly how to reproduce the issue 44 | - Explain how you started AdaptiveShell 45 | - Include the specific commands or actions used 46 | - Be precise about what you did and how you did it 47 | 48 | 3. **Examples and Evidence**: 49 | - Provide specific examples demonstrating the issue 50 | - Include code snippets using [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines) 51 | - Add screenshots or GIFs if relevant 52 | - For crashes, include the full error message in a code block or [gist](https://gist.github.com/) 53 | 54 | 4. **Expected vs Actual Behavior**: 55 | - Describe what you observed 56 | - Explain what you expected to happen 57 | - Clarify why the current behavior is problematic 58 | 59 | 5. **Context**: 60 | - When did the issue start? 61 | - Which version of AdaptiveShell are you using? 62 | - What's your operating system and version? 63 | - Are you using a virtual machine? If yes, specify the VM software and configurations 64 | 65 | 6. **Reproducibility**: 66 | - Can you consistently reproduce the issue? 67 | - If intermittent, describe the conditions when it occurs 68 | 69 | Remember: The more detailed your report, the faster we can identify and fix the issue! 70 | 71 | --- 72 | 73 | ## Useful Links 74 | 75 | - [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) 76 | -------------------------------------------------------------------------------- /apps/launcher/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@metro-ui/launcher", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "index.ts", 6 | "packageManager": "pnpm@10.26.1", 7 | "engines": { 8 | "node": ">=24.0.0" 9 | }, 10 | "scripts": { 11 | "test": "dotenv -c -- jest --passWithNoTests", 12 | "test:watch": "dotenv -c -- jest --watchAll", 13 | "start": "dotenv -c -- expo start", 14 | "build": "dotenv -c -- expo prebuild", 15 | "android": "dotenv -c -- expo run:android", 16 | "ios": "dotenv -c -- expo run:ios", 17 | "web": "dotenv -c -- expo start --web", 18 | "lint": "biome lint .", 19 | "typecheck": "tsc --noEmit", 20 | "format": "biome format --write .", 21 | "check": "biome check .", 22 | "check:fix": "biome check --write ." 23 | }, 24 | "jest": { 25 | "collectCoverage": true, 26 | "coverageProvider": "v8", 27 | "coverageReporters": [ 28 | "json" 29 | ], 30 | "collectCoverageFrom": [ 31 | "./src/**/*.*", 32 | "!**/node_modules/**" 33 | ], 34 | "preset": "jest-expo", 35 | "transformIgnorePatterns": [ 36 | "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)" 37 | ], 38 | "moduleNameMapper": { 39 | "^~/(.*)$": "/src/$1" 40 | }, 41 | "transform": { 42 | "^.+\\.tsx?$": [ 43 | "ts-jest", 44 | { 45 | "tsconfig": "./tsconfig.test.json" 46 | } 47 | ] 48 | } 49 | }, 50 | "dependencies": { 51 | "@bacons/text-decoder": "0.0.0", 52 | "@expo/vector-icons": "15.0.3", 53 | "@metro-ui/live-tiles": "workspace:*", 54 | "@react-navigation/bottom-tabs": "7.9.0", 55 | "@react-navigation/elements": "2.9.3", 56 | "@react-navigation/native": "7.1.26", 57 | "@shopify/flash-list": "2.2.0", 58 | "@tanstack/react-query": "5.90.12", 59 | "clsx": "2.1.1", 60 | "expo-application": "7.0.8", 61 | "expo-blur": "15.0.8", 62 | "expo-constants": "18.0.12", 63 | "expo-dev-client": "6.0.20", 64 | "expo-font": "14.0.10", 65 | "expo-haptics": "15.0.8", 66 | "expo-image": "3.0.11", 67 | "expo-linking": "8.0.11", 68 | "expo-localization": "17.0.8", 69 | "expo-router": "6.0.21", 70 | "expo-splash-screen": "31.0.13", 71 | "expo-status-bar": "3.0.9", 72 | "expo-symbols": "1.0.8", 73 | "expo-system-ui": "6.0.9", 74 | "expo": "54.0.30", 75 | "i18next": "25.7.3", 76 | "lodash": "4.17.21", 77 | "nativewind": "4.2.1", 78 | "react-dom": "19.0.0", 79 | "react-i18next": "16.5.0", 80 | "react-native-gesture-handler": "2.30.0", 81 | "react-native-keyboard-controller": "1.18.2", 82 | "react-native-reanimated": "3.17.4", 83 | "react-native-safe-area-context": "5.6.2", 84 | "react-native-screens": "4.19.0", 85 | "react-native": "0.79.5", 86 | "react": "19.0.0", 87 | "tailwind-merge": "3.4.0", 88 | "tailwindcss": "3.4.17" 89 | }, 90 | "devDependencies": { 91 | "@babel/core": "7.28.5", 92 | "@jest/globals": "30.2.0", 93 | "@testing-library/react-native": "13.1.1", 94 | "@types/jest": "29.5.14", 95 | "@types/lodash": "4.17.21", 96 | "@types/node": "24.10.4", 97 | "@types/react": "19.2.7", 98 | "babel-plugin-react-compiler": "19.1.0-rc.3", 99 | "babel-preset-expo": "54.0.9", 100 | "dotenv-cli": "11.0.0", 101 | "expo-module-scripts": "5.0.8", 102 | "jest-expo": "54.0.16", 103 | "jest": "29.7.0", 104 | "react-native-svg-transformer": "1.5.2", 105 | "ts-jest": "29.4.6", 106 | "ts-node": "10.9.2", 107 | "typescript": "5.9.3" 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json", 3 | "vcs": { 4 | "enabled": true, 5 | "clientKind": "git", 6 | "useIgnoreFile": true 7 | }, 8 | "files": { 9 | "ignoreUnknown": true, 10 | "includes": [ 11 | "**", 12 | "!**/node_modules", 13 | "!**/dist", 14 | "!**/build", 15 | "!**/coverage", 16 | "!**/.expo", 17 | "!**/.next", 18 | "!**/.now", 19 | "!**/.turbo", 20 | "!**/.tsup", 21 | "!**/public", 22 | "!**/*.lock", 23 | "!**/*.log", 24 | "!**/*.bak", 25 | "!**/android", 26 | "!**/ios", 27 | "!**/docs" 28 | ] 29 | }, 30 | "formatter": { 31 | "enabled": true, 32 | "formatWithErrors": false, 33 | "indentStyle": "space", 34 | "indentWidth": 4, 35 | "lineEnding": "lf", 36 | "lineWidth": 120, 37 | "attributePosition": "auto" 38 | }, 39 | "assist": { "actions": { "source": { "organizeImports": "on" } } }, 40 | "linter": { 41 | "enabled": true, 42 | "rules": { 43 | "recommended": true, 44 | "complexity": { 45 | "noBannedTypes": "error", 46 | "noUselessConstructor": "off", 47 | "noUselessTypeConstraint": "error" 48 | }, 49 | "correctness": { 50 | "noUnusedImports": "warn", 51 | "noUnusedVariables": "warn", 52 | "useExhaustiveDependencies": "warn", 53 | "useHookAtTopLevel": "error" 54 | }, 55 | "nursery": {}, 56 | "style": { 57 | "noNamespace": "error", 58 | "noNonNullAssertion": "warn", 59 | "useAsConstAssertion": "error", 60 | "useBlockStatements": "off", 61 | "useConsistentArrayType": { 62 | "level": "error", 63 | "options": { 64 | "syntax": "shorthand" 65 | } 66 | }, 67 | "useForOf": "error", 68 | "useImportType": "error", 69 | "useShorthandFunctionType": "error", 70 | "noUnusedTemplateLiteral": "error", 71 | "useExportType": "error", 72 | "useSingleVarDeclarator": "off", 73 | "noCommonJs": "warn", 74 | "useConsistentMemberAccessibility": "off" 75 | }, 76 | "suspicious": { 77 | "noConsole": "off", 78 | "noDebugger": "error", 79 | "noExplicitAny": "warn", 80 | "noNonNullAssertedOptionalChain": "warn", 81 | "noRedeclare": "error" 82 | }, 83 | "performance": { 84 | "noDelete": "warn" 85 | } 86 | } 87 | }, 88 | "javascript": { 89 | "formatter": { 90 | "jsxQuoteStyle": "double", 91 | "quoteProperties": "asNeeded", 92 | "trailingCommas": "none", 93 | "semicolons": "always", 94 | "arrowParentheses": "always", 95 | "bracketSpacing": true, 96 | "bracketSameLine": false, 97 | "quoteStyle": "single", 98 | "attributePosition": "auto" 99 | }, 100 | "globals": ["React"] 101 | }, 102 | "json": { 103 | "formatter": { 104 | "trailingCommas": "none" 105 | } 106 | }, 107 | "overrides": [ 108 | { 109 | "includes": ["**/*.cjs", "**/tailwind.config.ts", "**/metro.config.cjs", "**/babel.config.cjs"], 110 | "linter": { 111 | "rules": { 112 | "style": { 113 | "noCommonJs": "off" 114 | } 115 | } 116 | } 117 | }, 118 | { 119 | "includes": ["**/*.css"], 120 | "linter": { 121 | "rules": { 122 | "suspicious": { 123 | "noUnknownAtRules": "off" 124 | } 125 | } 126 | } 127 | } 128 | ] 129 | } 130 | -------------------------------------------------------------------------------- /packages/live-tiles/src/tiles/live-tiles.ts: -------------------------------------------------------------------------------- 1 | import type { Optional } from '../@types'; 2 | 3 | export enum LiveTileBranding { 4 | None = 0, 5 | Logo, 6 | Name, 7 | NameAndLogo 8 | } 9 | 10 | export enum AdaptiveTextStacking { 11 | Default = 0, 12 | Top, 13 | Center, 14 | Bottom 15 | } 16 | 17 | export enum AdaptiveImageAlign { 18 | Default = 0, 19 | Stretch, 20 | Left, 21 | Center, 22 | Right 23 | } 24 | 25 | export enum AdaptiveTextStyle { 26 | Default = 0, 27 | Caption, 28 | CaptionSubtle, 29 | Body, 30 | BodySubtle, 31 | Base, 32 | BaseSubtle, 33 | Subtitle, 34 | SubtitleSubtle, 35 | Title, 36 | TitleSubtle, 37 | TitleNumeral, 38 | Subheader, 39 | SubheaderSubtle, 40 | SubheaderNumeral, 41 | Header, 42 | HeaderSubtle, 43 | HeaderNumeral 44 | } 45 | export enum AdaptiveTextAlign { 46 | Default = 0, 47 | Auto, 48 | Left, 49 | Center, 50 | Right 51 | } 52 | 53 | export abstract class AdaptiveContent {} 54 | export class AdaptiveImage extends AdaptiveContent { 55 | public constructor( 56 | public readonly source: string, 57 | public readonly align: AdaptiveImageAlign = AdaptiveImageAlign.Default 58 | ) { 59 | super(); 60 | } 61 | } 62 | export class AdaptiveText extends AdaptiveContent { 63 | public constructor( 64 | public readonly text: string, 65 | public readonly wrap: boolean = false, 66 | public readonly style: AdaptiveTextStyle = AdaptiveTextStyle.Default, 67 | public readonly align: AdaptiveTextAlign = AdaptiveTextAlign.Default 68 | ) { 69 | super(); 70 | } 71 | } 72 | 73 | export abstract class LiveTileImage { 74 | public constructor( 75 | public readonly source: string, 76 | public readonly overlay: number = 20 77 | ) {} 78 | } 79 | export class LiveTileBadgeImage extends LiveTileImage {} 80 | export class LiveTilePeekImage extends LiveTileImage {} 81 | export class LiveTileBackgroundImage extends LiveTileImage {} 82 | 83 | export abstract class LiveTileContent {} 84 | export class LiveTileContentAdaptive extends LiveTileContent { 85 | public constructor( 86 | public readonly peekImage: Optional, 87 | public readonly backgroundImage: Optional, 88 | public readonly textStacking: AdaptiveTextStacking = AdaptiveTextStacking.Default, 89 | public readonly children: AdaptiveContent[] = [] 90 | ) { 91 | super(); 92 | } 93 | } 94 | 95 | /** 96 | * A tile that displays a single line of text with an optional badge. 97 | * 98 | * ![screenshot](https://learn.microsoft.com/en-us/previous-versions/windows/apps/images/hh761491.tilesquare150x150iconwithbadge(en-us,win.10).jpg) 99 | * 100 | *{@link https://learn.microsoft.com/en-us/previous-versions/windows/apps/hh761491(v=win.10)#tilesquare150x150iconwithbadge} 101 | * @extends {LiveTileContent} 102 | */ 103 | export class LiveTileContentIconWithBadge extends LiveTileContent { 104 | public constructor( 105 | public readonly badgeImage: Optional, 106 | public readonly text: string = '' 107 | ) { 108 | super(); 109 | } 110 | } 111 | 112 | /** 113 | * A tile that displays two rows of text either side by side or one above the other depending on the device/platform. 114 | * They were typically used for display the day and month the calendar app tile. 115 | * 116 | * ![screenshot](https://learn.microsoft.com/en-us/previous-versions/windows/apps/images/hh761491.tilesquareblock(en-us,win.10).png) 117 | * 118 | * {@link https://learn.microsoft.com/en-us/previous-versions/windows/apps/hh761491(v=win.10)#tilesquareblocktilesquare150x150block} 119 | * @extends {LiveTileContent} 120 | */ 121 | export class LiveTileContentTileSquareBlock extends LiveTileContent { 122 | public constructor( 123 | public readonly blockText: Optional, 124 | public readonly besideBlockText: Optional, 125 | public readonly text: string = '' 126 | ) { 127 | super(); 128 | } 129 | } 130 | 131 | export class LiveTileBinding { 132 | public constructor( 133 | public displayName: Optional, 134 | public branding: Optional, 135 | public content: LiveTileContent 136 | ) {} 137 | } 138 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - staging 9 | - 'dev/*' 10 | - 'fix/*' 11 | paths-ignore: 12 | - '**.md' 13 | - '**.txt' 14 | - 'LICENSE' 15 | 16 | env: 17 | CI: true 18 | # Make sure we disable husky as it might interfere with some operations here 19 | HUSKY: 0 20 | 21 | jobs: 22 | lint: 23 | name: 📋 Lint 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: 🕶️ Checkout repository 27 | uses: actions/checkout@v6 28 | 29 | - name: 🚀 Bootstrap 30 | uses: ./.github/common/bootstrap 31 | 32 | - name: 📋 Lint 33 | run: pnpm run lint 34 | 35 | typecheck: 36 | name: ✅ Typecheck 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: 🕶️ Checkout repository 40 | uses: actions/checkout@v6 41 | 42 | - name: 🚀 Bootstrap 43 | uses: ./.github/common/bootstrap 44 | 45 | - name: ✅ Typecheck 46 | run: pnpm run typecheck 47 | 48 | build: 49 | name: 🔨 Build 50 | needs: [lint, typecheck] 51 | runs-on: ubuntu-latest 52 | steps: 53 | - name: 🕶️ Checkout repository 54 | uses: actions/checkout@v6 55 | 56 | - name: 🚀 Bootstrap 57 | uses: ./.github/common/bootstrap 58 | 59 | - name: 🔬 Verify integrity 60 | run: npm audit signatures 61 | 62 | - name: 🔨 Build all 63 | run: pnpm run build 64 | 65 | test: 66 | name: 🧪 Test 67 | needs: [lint, typecheck] 68 | runs-on: ubuntu-latest 69 | steps: 70 | - name: 🕶️ Checkout repository 71 | uses: actions/checkout@v6 72 | 73 | - name: 🚀 Bootstrap 74 | uses: ./.github/common/bootstrap 75 | 76 | - name: 🧪 Test 77 | run: pnpm run test 78 | 79 | - name: 🦺 Codecov coverage reports 80 | if: ${{ !cancelled() }} 81 | uses: codecov/codecov-action@v5 82 | with: 83 | token: ${{ secrets.CODECOV_TOKEN }} 84 | slug: filiphsps/AdaptiveShell 85 | 86 | - name: 🦺 Upload test results to Codecov 87 | if: ${{ !cancelled() }} 88 | uses: codecov/test-results-action@v1 89 | with: 90 | token: ${{ secrets.CODECOV_TOKEN }} 91 | slug: filiphsps/AdaptiveShell 92 | 93 | release: 94 | name: 🚢 Release 95 | needs: [build, test] 96 | runs-on: ubuntu-latest 97 | if: github.ref == 'refs/heads/master' 98 | permissions: write-all 99 | concurrency: 100 | group: ${{ github.workflow }}-${{ github.ref }} 101 | env: 102 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 103 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 104 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 105 | steps: 106 | - name: 🕶️ Checkout repository 107 | uses: actions/checkout@v6 108 | with: 109 | fetch-depth: 0 110 | submodules: 'recursive' 111 | 112 | - name: 🚀 Bootstrap 113 | uses: ./.github/common/bootstrap 114 | 115 | - name: 🔬 Verify integrity 116 | run: npm audit signatures 117 | 118 | - name: 🔨 Build all 119 | run: pnpm run build:packages 120 | 121 | - name: 📦 Generate changeset(s) from commit messages 122 | run: pnpm changeset:conventional 123 | 124 | - name: 🤝 Create release PR or 📦 Publish 125 | id: changesets 126 | uses: changesets/action@v1 127 | with: 128 | createGithubReleases: true 129 | version: pnpm run version 130 | publish: pnpm run release 131 | setupGitUser: true 132 | commit: | 133 | ci(release): Release packages and bump versions 134 | 135 | Congratulations on the new release! 🎉 136 | This is an automated commit triggered by the `release` workflow. 137 | title: 🚀 Release packages 138 | 139 | - name: 🫨 Create Unstable Release 140 | if: steps.changesets.outputs.published != 'true' 141 | run: | 142 | git checkout master 143 | pnpm run version:unstable 144 | pnpm run release:unstable 145 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | --------------------------------------------------------------------------------