diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3000c36..01f6f650 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,7 @@ jobs: matrix: project: - helia-101 + - helia-angular - helia-browser-verified-fetch - helia-cjs - helia-electron @@ -86,6 +87,7 @@ jobs: matrix: project: - helia-101 + - helia-angular - helia-cjs - helia-browser-verified-fetch - helia-electron diff --git a/examples/helia-angular/.gitignore b/examples/helia-angular/.gitignore new file mode 100644 index 00000000..cc7b1413 --- /dev/null +++ b/examples/helia-angular/.gitignore @@ -0,0 +1,42 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/examples/helia-angular/README.md b/examples/helia-angular/README.md new file mode 100644 index 00000000..f280c6d9 --- /dev/null +++ b/examples/helia-angular/README.md @@ -0,0 +1,135 @@ +

+ + Helia logo + +

+ +

Using Helia inside an Angular.js app

+ +

+ +
+ Explore the docs + · + View Demo + · + Report Bug + · + Request Feature/Example +

+ +## Table of Contents + +See https://github.com/ipfs-examples/helia-examples#table-of-contents for more information about Helia examples. + +## About The Project + +- Read the [docs](https://ipfs.github.io/helia/modules/helia.html) +- Look into other [examples](https://github.com/ipfs-examples/helia-examples) to learn how to spawn a Helia node in Node.js and in the Browser +- Visit https://dweb-primer.ipfs.io to learn about IPFS and the concepts that underpin it +- Head over to https://proto.school to take interactive tutorials that cover core IPFS APIs +- Check out https://docs.ipfs.io for tips, how-tos and more +- See https://blog.ipfs.io for news and more +- Need help? Please ask 'How do I?' questions on https://discuss.ipfs.io + +## Getting Started + +### Prerequisites + +https://github.com/ipfs-examples/helia-examples#prerequisites + +### Installation and Running example + +```console +> npm install +> ng serve +``` + +Now open your browser at `http://localhost:4200/` + +## Available Scripts + +In the project directory, you can run: + +### Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. + +### Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +### Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. + +### Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +### Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. + +### Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. + + +## Usage + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.1.4. integrated with `helia`. + +You can start editing the page by modifying `src/app/app.component.html`. The page auto-updates as you edit the file. + +### Learn More + +To learn more about Angular.js, take a look at the following resources: + +- [Angular.js Documentation](https://angular.dev/overview) - learn about Angular.js features. +- [Learn Angular.js](https://angular.dev/tutorials/learn-angular) - an interactive Angular.js tutorial. + +You can check out [the Angular.js GitHub repository](https://github.com/angular) - your feedback and contributions are welcome! + +### Deploy on Vercel + +The easiest way to deploy your Angular.js app is to use the [Vercel Platform](https://vercel.com/solutions/angular#the-easiest-way-to-deploy). + +Check out our [Angular.js deployment documentation](https://angular.dev/tools/cli/deployment) for more details. + +_For more examples, please refer to the [Documentation](https://github.com/ipfs-examples/helia-examples#documentation)_ + +## Documentation + +- [IPFS Primer](https://dweb-primer.ipfs.io/) +- [IPFS Docs](https://docs.ipfs.io/) +- [Tutorials](https://proto.school) +- [More examples](https://github.com/ipfs-examples/helia-examples) +- [API - Helia](https://ipfs.github.io/helia/modules/helia.html) +- [API - @helia/unixfs](https://ipfs.github.io/helia-unixfs/modules/helia.html) + +## Contributing + +Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. + +1. Fork the IPFS Project +2. Create your Feature Branch (`git checkout -b feature/amazing-feature`) +3. Commit your Changes (`git commit -a -m 'feat: add some amazing feature'`) +4. Push to the Branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## Want to hack on IPFS? + +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) + +The IPFS implementation in JavaScript needs your help! There are a few things you can do right now to help out: + +Read the [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md) and [JavaScript Contributing Guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md). + +- **Check out existing issues** The [issue list](https://github.com/ipfs/helia/issues) has many that are marked as ['help wanted'](https://github.com/ipfs/helia/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22help+wanted%22) or ['difficulty:easy'](https://github.com/ipfs/helia/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Adifficulty%3Aeasy) which make great starting points for development, many of which can be tackled with no prior IPFS knowledge +- **Look at the [Helia Roadmap](https://github.com/ipfs/helia/blob/main/ROADMAP.md)** This are the high priority items being worked on right now +- **Perform code reviews** More eyes will help + a. speed the project along + b. ensure quality, and + c. reduce possible future bugs +- **Add tests**. There can never be enough tests \ No newline at end of file diff --git a/examples/helia-angular/angular.json b/examples/helia-angular/angular.json new file mode 100644 index 00000000..c3e1e60d --- /dev/null +++ b/examples/helia-angular/angular.json @@ -0,0 +1,104 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "helia-angular": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/helia-angular", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ], + "scripts": [], + "server": "src/main.server.ts", + "prerender": true, + "ssr": { + "entry": "server.ts" + } + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kB", + "maximumError": "4kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "helia-angular:build:production" + }, + "development": { + "buildTarget": "helia-angular:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ], + "scripts": [] + } + } + } + } + }, + "cli": { + "analytics": "d305ee0c-891f-4040-af7e-020df473e208" + } +} diff --git a/examples/helia-angular/package.json b/examples/helia-angular/package.json new file mode 100644 index 00000000..29b45155 --- /dev/null +++ b/examples/helia-angular/package.json @@ -0,0 +1,45 @@ +{ + "name": "helia-angular", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test", + "serve:ssr:helia-angular": "node dist/helia-angular/server/server.mjs" + }, + "private": true, + "dependencies": { + "@angular/animations": "^18.1.0", + "@angular/common": "^18.1.0", + "@angular/compiler": "^18.1.0", + "@angular/core": "^18.1.0", + "@angular/forms": "^18.1.0", + "@angular/platform-browser": "^18.1.0", + "@angular/platform-browser-dynamic": "^18.1.0", + "@angular/platform-server": "^18.1.0", + "@angular/router": "^18.1.0", + "@angular/ssr": "^18.1.4", + "express": "^4.18.2", + "helia": "^5.1.1", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.3" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^18.1.4", + "@angular/cli": "^18.1.4", + "@angular/compiler-cli": "^18.1.0", + "@types/express": "^4.17.17", + "@types/jasmine": "~5.1.0", + "@types/node": "^18.18.0", + "jasmine-core": "~5.1.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.5.2" + } +} diff --git a/examples/helia-angular/public/favicon.ico b/examples/helia-angular/public/favicon.ico new file mode 100644 index 00000000..3580aaf4 Binary files /dev/null and b/examples/helia-angular/public/favicon.ico differ diff --git a/examples/helia-angular/server.ts b/examples/helia-angular/server.ts new file mode 100644 index 00000000..1a0df5e0 --- /dev/null +++ b/examples/helia-angular/server.ts @@ -0,0 +1,57 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { CommonEngine } from '@angular/ssr'; +import express from 'express'; +import { fileURLToPath } from 'node:url'; +import { dirname, join, resolve } from 'node:path'; +import bootstrap from './src/main.server'; + +// The Express app is exported so that it can be used by serverless Functions. +export function app(): express.Express { + const server = express(); + const serverDistFolder = dirname(fileURLToPath(import.meta.url)); + const browserDistFolder = resolve(serverDistFolder, '../browser'); + const indexHtml = join(serverDistFolder, 'index.server.html'); + + const commonEngine = new CommonEngine(); + + server.set('view engine', 'html'); + server.set('views', browserDistFolder); + + // Example Express Rest API endpoints + // server.get('/api/**', (req, res) => { }); + // Serve static files from /browser + server.get('**', express.static(browserDistFolder, { + maxAge: '1y', + index: 'index.html', + })); + + // All regular routes use the Angular engine + server.get('**', (req, res, next) => { + const { protocol, originalUrl, baseUrl, headers } = req; + + commonEngine + .render({ + bootstrap, + documentFilePath: indexHtml, + url: `${protocol}://${headers.host}${originalUrl}`, + publicPath: browserDistFolder, + providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], + }) + .then((html) => res.send(html)) + .catch((err) => next(err)); + }); + + return server; +} + +function run(): void { + const port = process.env['PORT'] || 4000; + + // Start up the Node server + const server = app(); + server.listen(port, () => { + console.log(`Node Express server listening on http://localhost:${port}`); + }); +} + +run(); diff --git a/examples/helia-angular/src/app/app.component.css b/examples/helia-angular/src/app/app.component.css new file mode 100644 index 00000000..94782ef6 --- /dev/null +++ b/examples/helia-angular/src/app/app.component.css @@ -0,0 +1,168 @@ + + :host { + --bright-blue: oklch(51.01% 0.274 263.83); + --electric-violet: oklch(53.18% 0.28 296.97); + --french-violet: oklch(47.66% 0.246 305.88); + --vivid-pink: oklch(69.02% 0.277 332.77); + --hot-red: oklch(61.42% 0.238 15.34); + --orange-red: oklch(63.32% 0.24 31.68); + + --gray-900: oklch(19.37% 0.006 300.98); + --gray-700: oklch(36.98% 0.014 302.71); + --gray-400: oklch(70.9% 0.015 304.04); + + --red-to-pink-to-purple-vertical-gradient: linear-gradient( + 180deg, + var(--orange-red) 0%, + var(--vivid-pink) 50%, + var(--electric-violet) 100% + ); + + --red-to-pink-to-purple-horizontal-gradient: linear-gradient( + 90deg, + var(--orange-red) 0%, + var(--vivid-pink) 50%, + var(--electric-violet) 100% + ); + + --pill-accent: var(--bright-blue); + + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol"; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + + h1 { + font-size: 3.125rem; + color: var(--gray-900); + font-weight: 500; + line-height: 100%; + letter-spacing: -0.125rem; + margin: 0; + font-family: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol"; + } + + p { + margin: 0; + color: var(--gray-700); + } + + main { + width: 100%; + min-height: 100%; + display: flex; + justify-content: center; + align-items: center; + padding: 1rem; + box-sizing: inherit; + position: relative; + } + + .angular-logo { + max-width: 9.2rem; + } + + .content { + display: flex; + justify-content: space-around; + width: 100%; + max-width: 700px; + margin-bottom: 3rem; + } + + .content h1 { + margin-top: 1.75rem; + } + + .content p { + margin-top: 1.5rem; + } + + .divider { + width: 1px; + background: var(--red-to-pink-to-purple-vertical-gradient); + margin-inline: 0.5rem; + } + + .pill-group { + display: flex; + flex-direction: column; + align-items: start; + flex-wrap: wrap; + gap: 1.25rem; + } + + .pill { + display: flex; + align-items: center; + --pill-accent: var(--bright-blue); + background: color-mix(in srgb, var(--pill-accent) 5%, transparent); + color: var(--pill-accent); + padding-inline: 0.75rem; + padding-block: 0.375rem; + border-radius: 2.75rem; + border: 0; + transition: background 0.3s ease; + font-family: var(--inter-font); + font-size: 0.875rem; + font-style: normal; + font-weight: 500; + line-height: 1.4rem; + letter-spacing: -0.00875rem; + text-decoration: none; + } + + .pill:hover { + background: color-mix(in srgb, var(--pill-accent) 15%, transparent); + } + + .pill-group .pill:nth-child(6n + 1) { + --pill-accent: var(--bright-blue); + } + .pill-group .pill:nth-child(6n + 2) { + --pill-accent: var(--french-violet); + } + .pill-group .pill:nth-child(6n + 3), + .pill-group .pill:nth-child(6n + 4), + .pill-group .pill:nth-child(6n + 5) { + --pill-accent: var(--hot-red); + } + + .pill-group svg { + margin-inline-start: 0.25rem; + } + + .social-links { + display: flex; + align-items: center; + gap: 0.73rem; + margin-top: 1.5rem; + } + + .social-links path { + transition: fill 0.3s ease; + fill: var(--gray-400); + } + + .social-links a:hover svg path { + fill: var(--gray-900); + } + + @media screen and (max-width: 650px) { + .content { + flex-direction: column; + width: max-content; + } + + .divider { + height: 1px; + width: 100%; + background: var(--red-to-pink-to-purple-horizontal-gradient); + margin-block: 1.5rem; + } + } \ No newline at end of file diff --git a/examples/helia-angular/src/app/app.component.html b/examples/helia-angular/src/app/app.component.html new file mode 100644 index 00000000..dc9040ef --- /dev/null +++ b/examples/helia-angular/src/app/app.component.html @@ -0,0 +1,199 @@ + + + + + + + + + +
+
+
+ +

Hello, {{ title }}

+

Congratulations! Your app is running. 🎉

+ +
+
+

Starting Helia...

+
+ +
+

ID: {{ id }}

+

Status: {{ id ? 'Online' : 'Offline' }}

+
+
+ +
+ + +

IPFS Documentation →

+

+ Learn how to build the future of the internet +

+
+ + +

IPFS Tutorials →

+

+ Interactive tutorials on decentralized web protocols +

+
+
+
+ +
+
+ @for (item of [ + { title: 'Explore the Docs', link: 'https://angular.dev' }, + { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, + { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, + { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, + { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, + ]; track item.title) { + + {{ item.title }} + + + + + } +
+ +
+
+
+ + + + + + + + + + diff --git a/examples/helia-angular/src/app/app.component.spec.ts b/examples/helia-angular/src/app/app.component.spec.ts new file mode 100644 index 00000000..02df5d02 --- /dev/null +++ b/examples/helia-angular/src/app/app.component.spec.ts @@ -0,0 +1,29 @@ +import { TestBed } from '@angular/core/testing'; +import { HeliaComponent } from './helia.component'; + +describe('HeliaComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HeliaComponent], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(HeliaComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have the 'helia-angular' title`, () => { + const fixture = TestBed.createComponent(HeliaComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('helia-angular'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(HeliaComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, helia-angular'); + }); +}); diff --git a/examples/helia-angular/src/app/app.config.server.ts b/examples/helia-angular/src/app/app.config.server.ts new file mode 100644 index 00000000..b4d57c94 --- /dev/null +++ b/examples/helia-angular/src/app/app.config.server.ts @@ -0,0 +1,11 @@ +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering } from '@angular/platform-server'; +import { appConfig } from './app.config'; + +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering() + ] +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/examples/helia-angular/src/app/app.config.ts b/examples/helia-angular/src/app/app.config.ts new file mode 100644 index 00000000..52cd710c --- /dev/null +++ b/examples/helia-angular/src/app/app.config.ts @@ -0,0 +1,9 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { routes } from './app.routes'; +import { provideClientHydration } from '@angular/platform-browser'; + +export const appConfig: ApplicationConfig = { + providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration()] +}; diff --git a/examples/helia-angular/src/app/app.routes.ts b/examples/helia-angular/src/app/app.routes.ts new file mode 100644 index 00000000..dc39edb5 --- /dev/null +++ b/examples/helia-angular/src/app/app.routes.ts @@ -0,0 +1,3 @@ +import { Routes } from '@angular/router'; + +export const routes: Routes = []; diff --git a/examples/helia-angular/src/app/helia.component.ts b/examples/helia-angular/src/app/helia.component.ts new file mode 100644 index 00000000..30f9677e --- /dev/null +++ b/examples/helia-angular/src/app/helia.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { isPlatformBrowser, CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [RouterOutlet, CommonModule], + templateUrl: './app.component.html', + styleUrl: './app.component.css' +}) +export class HeliaComponent implements OnInit { + title = 'helia-angular'; + helia: any | null = null; + id: string | null = null; + isOnline: boolean = false; + + constructor(@Inject(PLATFORM_ID) private platformId: any) {} + + async ngOnInit() { + if (isPlatformBrowser(this.platformId)) { + try { + const { createHelia } = await import('helia'); + this.helia = await createHelia(); + console.log('Helia initialized:', this.helia); + + // Fetch the node's ID + const peerId = await this.helia.libp2p.peerId; + this.id = peerId.toString(); + } catch (error) { + console.error('Error initializing Helia:', error); + } + } + } +} diff --git a/examples/helia-angular/src/index.html b/examples/helia-angular/src/index.html new file mode 100644 index 00000000..797ca765 --- /dev/null +++ b/examples/helia-angular/src/index.html @@ -0,0 +1,13 @@ + + + + + HeliaAngularExample + + + + + + + + diff --git a/examples/helia-angular/src/main.server.ts b/examples/helia-angular/src/main.server.ts new file mode 100644 index 00000000..05bb30c6 --- /dev/null +++ b/examples/helia-angular/src/main.server.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { HeliaComponent } from './app/helia.component'; +import { config } from './app/app.config.server'; + +const bootstrap = () => bootstrapApplication(HeliaComponent, config); + +export default bootstrap; diff --git a/examples/helia-angular/src/main.ts b/examples/helia-angular/src/main.ts new file mode 100644 index 00000000..a2e73e1c --- /dev/null +++ b/examples/helia-angular/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { HeliaComponent } from './app/helia.component'; + +bootstrapApplication(HeliaComponent, appConfig) + .catch((err) => console.error(err)); diff --git a/examples/helia-angular/src/styles.css b/examples/helia-angular/src/styles.css new file mode 100644 index 00000000..eeae72af --- /dev/null +++ b/examples/helia-angular/src/styles.css @@ -0,0 +1,185 @@ +/* You can add global styles to this file, and also import other style files */ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + } + + a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; + } + a:hover { + color: #535bf2; + } + + + h1 { + font-size: 3.2em; + line-height: 1.1; + } + + button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; + } + button:hover { + border-color: #646cff; + } + button:focus, + button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; + } + + .container { + min-height: 100vh; + padding: 0 0.5rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; + } + + .main { + padding: 5rem 0; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + + .footer { + width: 100%; + height: 100px; + border-top: 1px solid #eaeaea; + display: flex; + justify-content: center; + align-items: center; + } + + .footer a { + display: flex; + justify-content: center; + align-items: center; + flex-grow: 1; + } + + .title a { + color: #0070f3; + text-decoration: none; + } + + .title a:hover, + .title a:focus, + .title a:active { + text-decoration: underline; + } + + .title { + margin: 0; + line-height: 1.15; + font-size: 4rem; + } + + .title, + .description { + text-align: center; + } + + .description { + line-height: 1.5; + font-size: 1.5rem; + } + + .code { + background: #fafafa; + border-radius: 5px; + padding: 0.75rem; + font-size: 1.1rem; + font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, + Bitstream Vera Sans Mono, Courier New, monospace; + } + + .grid { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + max-width: 800px; + margin-top: 3rem; + } + + .card { + margin: 1rem; + padding: 1.5rem; + text-align: left; + color: inherit; + text-decoration: none; + border: 1px solid #eaeaea; + border-radius: 10px; + transition: color 0.15s ease, border-color 0.15s ease; + width: 45%; + } + + .card:hover, + .card:focus, + .card:active { + color: #0070f3; + border-color: #0070f3; + } + + .card h2 { + margin: 0 0 1rem 0; + font-size: 1.5rem; + } + + .card p { + margin: 0; + font-size: 1.25rem; + line-height: 1.5; + } + + .logo { + height: 1em; + margin-left: 0.5rem; + } + + @media (max-width: 600px) { + .grid { + width: 100%; + flex-direction: column; + } + } + + @media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } + } diff --git a/examples/helia-angular/tsconfig.app.json b/examples/helia-angular/tsconfig.app.json new file mode 100644 index 00000000..cdc0b287 --- /dev/null +++ b/examples/helia-angular/tsconfig.app.json @@ -0,0 +1,19 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [ + "node" + ] + }, + "files": [ + "src/main.ts", + "src/main.server.ts", + "server.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/examples/helia-angular/tsconfig.json b/examples/helia-angular/tsconfig.json new file mode 100644 index 00000000..a9ae8b06 --- /dev/null +++ b/examples/helia-angular/tsconfig.json @@ -0,0 +1,32 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "lib": [ + "ES2022", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/examples/helia-angular/tsconfig.spec.json b/examples/helia-angular/tsconfig.spec.json new file mode 100644 index 00000000..5fb748d9 --- /dev/null +++ b/examples/helia-angular/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +}