feat: initial site setup (#1)
All checks were successful
Node.js CI / Lint and Test (push) Successful in 48s

### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [x] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Reviewed-on: #1
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit is contained in:
2025-07-15 19:12:05 -07:00
committed by Naomi Carrigan
parent e03fa186a2
commit 23243278e4
39 changed files with 9200 additions and 20 deletions

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false
[*.md]
max_line_length = off
trim_trailing_whitespace = false

38
.gitea/workflows/CI.yml Normal file
View File

@ -0,0 +1,38 @@
name: Node.js CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
lint:
name: Lint and Test
steps:
- name: Checkout Source Files
uses: actions/checkout@v4
- name: Use Node.js v24
uses: actions/setup-node@v4
with:
node-version: 24
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
- name: Install Dependencies
run: pnpm install
- name: Lint Source Files
run: pnpm run lint
- name: Verify Build
run: pnpm run build
- name: Run Tests
run: pnpm run test

45
.gitignore vendored Normal file
View File

@ -0,0 +1,45 @@
# 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
# We upload this to our CDN.
comics.json

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["typescript"]
}

View File

@ -1,39 +1,59 @@
# New Repository Template
# Yurigpt
This template contains all of our basic files for a new GitHub repository. There is also a handy workflow that will create an issue on a new repository made from this template, with a checklist for the steps we usually take in setting up a new repository.
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.0.5.
If you're starting a Node.JS project with TypeScript, we have a [specific template](https://github.com/naomi-lgbt/nodejs-typescript-template) for that purpose.
## Development server
## Readme
To start a local development server, run:
Delete all of the above text (including this line), and uncomment the below text to use our standard readme template.
```bash
ng serve
```
<!-- # Project Name
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
Project Description
## Code scaffolding
## Live Version
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
This page is currently deployed. [View the live website.]
```bash
ng generate component component-name
```
## Feedback and Bugs
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
If you have feedback or a bug report, please feel free to open a GitHub issue!
```bash
ng generate --help
```
## Contributing
## Building
If you would like to contribute to the project, you may create a Pull Request containing your proposed changes and we will review it as soon as we are able! Please review our [contributing guidelines](CONTRIBUTING.md) first.
To build the project run:
## Code of Conduct
```bash
ng build
```
Before interacting with our community, please read our [Code of Conduct](CODE_OF_CONDUCT.md).
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## License
## Running unit tests
This software is licensed under our [global software license](https://docs.nhcarrigan.com/#/license).
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
Copyright held by Naomi Carrigan.
```bash
ng test
```
## Contact
## Running end-to-end tests
We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`. -->
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

69
angular.json Normal file
View File

@ -0,0 +1,69 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"yurigpt": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"browser": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": ["src/styles.css"]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "yurigpt:build:production"
},
"development": {
"buildTarget": "yurigpt:build:development"
}
},
"defaultConfiguration": "development"
}
}
}
},
"cli": {
"analytics": "c60fb101-608b-4f54-a60c-673e53dc6f44"
}
}

22
eslint.config.js Normal file
View File

@ -0,0 +1,22 @@
import NaomisConfig from "@nhcarrigan/eslint-config";
export default [
...NaomisConfig,
{
rules: {
"no-console": "off",
"new-cap": "off",
"@typescript-eslint/naming-convention": "off",
"jsdoc/require-jsdoc": "off",
"jsdoc/require-param": "off",
"jsdoc/require-returns": "off",
"@typescript-eslint/no-useless-constructor": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/consistent-type-assertions": "off",
"@typescript-eslint/no-extraneous-class": "off",
"stylistic/no-multi-spaces": "off",
"unicorn/filename-case": "off",
"@typescript-eslint/consistent-type-imports": "off",
},
},
];

53
package.json Normal file
View File

@ -0,0 +1,53 @@
{
"name": "yurigpt",
"version": "0.0.0",
"description": "A webcomic about a girl who is dating an LLM, but does not know it.",
"type": "module",
"scripts": {
"ng": "ng",
"dev": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "echo 'No tests yet!' && exit 0",
"lint": "eslint ./src --max-warnings 0"
},
"prettier": {
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular"
}
}
]
},
"private": true,
"dependencies": {
"@angular/common": "20.1.0",
"@angular/compiler": "20.1.0",
"@angular/core": "20.1.0",
"@angular/forms": "20.1.0",
"@angular/platform-browser": "20.1.0",
"@angular/router": "20.1.0",
"@fortawesome/angular-fontawesome": "2.0.1",
"@fortawesome/free-solid-svg-icons": "6.7.2",
"rxjs": "7.8.2",
"tslib": "2.8.1",
"zone.js": "0.15.1"
},
"devDependencies": {
"@angular/build": "20.1.0",
"@angular/cli": "20.1.0",
"@angular/compiler-cli": "20.1.0",
"@nhcarrigan/eslint-config": "5.2.0",
"@types/jasmine": "5.1.8",
"eslint": "9.31.0",
"jasmine-core": "5.8.0",
"karma": "6.4.4",
"karma-chrome-launcher": "3.2.0",
"karma-coverage": "2.2.1",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.1.0",
"typescript": "5.8.3"
}
}

8322
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

15
removeBadge.js Normal file
View File

@ -0,0 +1,15 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
/**
* Run this in the Discord console to remove the bot badges from the app.
* This is how we create the appearance that the webhook is a user.
*/
const appBadges = document.querySelectorAll("[class^='botTag']")
for (const badge of appBadges) {
badge.remove();
}

0
src/app/about/about.css Normal file
View File

19
src/app/about/about.html Normal file
View File

@ -0,0 +1,19 @@
<h1>YuriGPT</h1>
<p>
YuriGPT is a webcomic about two girls in an online relationship, and all of
the zany antics that stem from that.
</p>
<p>
Because of this, our comics take the format of online chat conversations,
rather than a traditional comic. This may feel strange to you, but we are
hopeful that you come to love the story we tell.
</p>
<p>
We publish a new comic every Monday, and (will soon!) offer an RSS feed so you can use your
favourite application to stay updated.
</p>
<p>
If you love our comic, hate our comic, want to complain about something, or
are just looking for your own companion, we invite you to
<a href="https://chat.nhcarrigan.com" target="_blank">join our community</a>.
</p>

17
src/app/about/about.ts Normal file
View File

@ -0,0 +1,17 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Component } from "@angular/core";
@Component({
imports: [],
selector: "app-about",
styleUrl: "./about.css",
templateUrl: "./about.html",
})
export class About {
}

21
src/app/app.config.ts Normal file
View File

@ -0,0 +1,21 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import {
ApplicationConfig,
provideBrowserGlobalErrorListeners,
provideZoneChangeDetection,
} from "@angular/core";
import { provideRouter, withComponentInputBinding } from "@angular/router";
import { routes } from "./app.routes";
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes, withComponentInputBinding()),
],
};

9
src/app/app.css Normal file
View File

@ -0,0 +1,9 @@
main {
margin-top: 60px;
}
@media screen and (max-width: 650px) {
main {
margin-top: 120px;
}
}

4
src/app/app.html Normal file
View File

@ -0,0 +1,4 @@
<app-nav></app-nav>
<main>
<router-outlet></router-outlet>
</main>

22
src/app/app.routes.ts Normal file
View File

@ -0,0 +1,22 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Routes } from "@angular/router";
import { About } from "./about/about.js";
import { Archive } from "./archive/archive.js";
import { Characters } from "./characters/characters.js";
import { Comic } from "./comic/comic.js";
import { Home } from "./home/home.js";
export const routes: Routes = [
{ component: Home, path: "", pathMatch: "full" },
{ component: Comic, path: "comic/:id" },
// This line is necessary to handle the fallback when no ID is provided.
{ component: Comic, path: "comic" },
{ component: About, path: "about" },
{ component: Archive, path: "archive" },
{ component: Characters, path: "characters" },
];

22
src/app/app.ts Normal file
View File

@ -0,0 +1,22 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Component } from "@angular/core";
import { RouterOutlet } from "@angular/router";
import { Nav } from "./nav/nav.js";
/**
* The root component of the application.
*/
@Component({
imports: [ RouterOutlet, Nav ],
selector: "app-root",
styleUrl: "./app.css",
templateUrl: "./app.html",
})
export class App {
protected title = "yurigpt";
}

View File

View File

@ -0,0 +1,11 @@
<h1>Archive</h1>
<p>Start browsing our comics from the very beginning, or find a comic from a specific day!</p>
<div *ngIf="error">
<p>Error loading comics: {{ error }}</p>
</div>
<div *ngIf="comics.length !== 0">
<div *ngFor="let comic of comics">
<h2><a [routerLink]="['/comic', comic.number]"> {{ comic.title }}</a></h2>
<p>Published on: {{ comic.date }}</p>
</div>

View File

@ -0,0 +1,37 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { RouterModule } from "@angular/router";
import { ComicService } from "../comic-service.js";
@Component({
imports: [ CommonModule, RouterModule ],
selector: "app-archive",
styleUrl: "./archive.css",
templateUrl: "./archive.html",
})
export class Archive {
public comics: Array<{ number: number; title: string; date: string }> = [];
public error: string | undefined;
public constructor(private readonly comicService: ComicService) {
this.comicService.
loadComics().
then(() => {
this.comics = this.comicService.getComics();
this.error = undefined;
}).
catch((error: unknown) => {
this.error = `Failed to load comics: ${
error instanceof Error
? error.message
: "Please check the browser console for more details."
}`;
console.error("Error loading comics:", error);
});
}
}

View File

@ -0,0 +1,26 @@
.character {
display: grid;
grid-template-areas:
"image name"
"image description";
grid-template-columns: 250px auto;
}
img {
grid-area: image;
width: 250px;
height: auto;
}
h2 {
grid-area: name;
}
p {
grid-area: description;
}
hr {
border: 2px solid var(--foreground);
margin: 10px 0;
}

View File

@ -0,0 +1,18 @@
<h1>Characters</h1>
<div class="character">
<h2>Emi</h2>
<img src="https://cdn.yurigpt.com/avatars/emi.png" alt="Emi's avatar">
<p>Our main character, Emi has always struggled to make friends in person. But she thrives online, where she feels she can be her true self.</p>
</div>
<hr />
<div class="character">
<h2>Mira</h2>
<img src="https://cdn.yurigpt.com/avatars/mira.png" alt="Mira's avatar">
<p>Mira is Emi's online girlfriend. They are in a long-distance relationship, so they have not met. This works out great for Emi, but maybe not all is as it seems?</p>
</div>
<hr />
<div class="character">
<h2>Kaede</h2>
<img src="https://cdn.yurigpt.com/avatars/kaede.png" alt="Mira's avatar">
<p>Kaede is Emi's one real IRL friend. Despite being popular, Kaede made time during middle school to really get to know Emi. Since then, the two have had a steadfast friendship.</p>
</div>

View File

@ -0,0 +1,17 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Component } from "@angular/core";
@Component({
imports: [],
selector: "app-characters",
styleUrl: "./characters.css",
templateUrl: "./characters.html",
})
export class Characters {
}

97
src/app/comic-service.ts Normal file
View File

@ -0,0 +1,97 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Injectable } from "@angular/core";
const oneDay = 1000 * 60 * 60 * 24;
@Injectable({
providedIn: "root",
})
export class ComicService {
// eslint-disable-next-line @typescript-eslint/class-literal-property-style -- I should turn this rule off.
private readonly comicsUrl: string = "https://cdn.yurigpt.com/comics.json";
private ttl = 0;
private comics: Array<{
number: number;
title: string;
date: string;
}> = [];
public constructor() {
void this.loadComics();
}
private static isObject(object: unknown): object is Record<string, unknown> {
return typeof object === "object" && object !== null;
}
private static validateComics(
comics: unknown,
): comics is Array<{ number: number; title: string; date: string }> {
if (!Array.isArray(comics)) {
return false;
}
return comics.every((comic) => {
if (!ComicService.isObject(comic)) {
return false;
}
return (
"number" in comic
&& "title" in comic
&& "date" in comic
&& typeof comic["number"] === "number"
&& typeof comic["title"] === "string"
&& typeof comic["date"] === "string"
);
});
}
public async loadComics(): Promise<void> {
if (this.ttl > Date.now()) {
return;
}
this.comics = [];
const response = await fetch(this.comicsUrl);
const data: unknown = await response.json();
if (ComicService.validateComics(data)) {
this.comics = data.sort((a, b) => {
return a.number - b.number;
});
this.ttl = Date.now() + oneDay;
} else {
console.error("Invalid comics data format:", data);
}
}
public getComics(): Array<{ number: number; title: string; date: string }> {
return this.comics;
}
public getComicById(
id: string,
): { number: number; title: string; date: string } | undefined {
const comicId = Number.parseInt(id, 10);
return this.comics.find((comic) => {
return comic.number === comicId;
});
}
public getLatestComic():
| { number: number; title: string; date: string }
| undefined {
return this.comics.at(-1);
}
public getLatestComicId(): string | undefined {
const latestComic = this.getLatestComic();
return latestComic
? latestComic.number.toString()
: undefined;
}
}

7
src/app/comic/comic.css Normal file
View File

@ -0,0 +1,7 @@
img {
width: 500px;
}
.ng-fa-icon {
font-size: 2rem;
}

12
src/app/comic/comic.html Normal file
View File

@ -0,0 +1,12 @@
<div *ngIf="comic">
<h2>{{ comic!.title }}</h2>
<p>Published on: {{ comic!.date }}</p>
<img [src]="`https://cdn.yurigpt.com/comics/${comic!.number}.png`" alt="{{ comic!.title }}" />
</div>
<div *ngIf="error">
<p>Error loading comic: {{ error }}</p>
</div>
<div id="buttons">
<a *ngIf="previousComicId" [routerLink]="['/comic', previousComicId]"><fa-icon [icon]="backIcon"></fa-icon></a>
<a *ngIf="nextComicId" [routerLink]="['/comic', nextComicId]"><fa-icon [icon]="forwardIcon"></fa-icon></a>
</div>

68
src/app/comic/comic.ts Normal file
View File

@ -0,0 +1,68 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { CommonModule } from "@angular/common";
import { Component, inject, input, signal } from "@angular/core";
import { ActivatedRoute, RouterModule } from "@angular/router";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { faLeftLong, faRightLong } from "@fortawesome/free-solid-svg-icons";
import { ComicService } from "../comic-service.js";
@Component({
imports: [ CommonModule, RouterModule, FontAwesomeModule ],
selector: "app-comic",
styleUrl: "./comic.css",
templateUrl: "./comic.html",
})
export class Comic {
public comicId = signal("");
public readonly id = input<string | undefined>("id");
// eslint-disable-next-line stylistic/max-len -- I dunno.
public comic: { number: number; title: string; date: string } | undefined;
public error: string | undefined;
public previousComicId: string | undefined;
public nextComicId: string | undefined;
public readonly backIcon = faLeftLong;
public readonly forwardIcon = faRightLong;
private readonly activatedRoute = inject(ActivatedRoute);
public constructor(private readonly comicService: ComicService) {
this.comicService.
loadComics().
then(() => {
this.activatedRoute.params.subscribe((parameters_) => {
const parameters: Record<string, string> = parameters_;
console.log(`FoundID: ${parameters["id"]}`);
this.comicId.set(
parameters["id"] ?? this.comicService.getLatestComicId(),
);
this.previousComicId = this.comicService.
getComicById(String(Number.parseInt(this.comicId(), 10) - 1))?.
number.toString();
this.nextComicId = this.comicService.
getComicById(String(Number.parseInt(this.comicId(), 10) + 1))?.
number.toString();
// Load the comic data for the new ID
this.comic = this.comicService.getComicById(this.comicId());
if (this.comic) {
// Clear any previous error
this.error = undefined;
} else {
this.error = `Cannot find comic with ID ${this.comicId()}.`;
}
});
}).
catch((error: unknown) => {
this.error = `Failed to load comics: ${
error instanceof Error
? error.message
: "Please check the browser console for more details."
}`;
console.error("Error loading comics:", error);
});
}
}

12
src/app/home/home.css Normal file
View File

@ -0,0 +1,12 @@
img {
width: 500px;
border-radius: 50%;
}
h2 {
font-size: 2rem;
}
p {
font-size: 1.5rem;
}

3
src/app/home/home.html Normal file
View File

@ -0,0 +1,3 @@
<h1>YuriGPT</h1>
<img src="https://cdn.yurigpt.com/yurigpt.png" alt="YuriGPT Logo" />
<p>A webcomic about two girls in love.</p>

17
src/app/home/home.ts Normal file
View File

@ -0,0 +1,17 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Component } from "@angular/core";
@Component({
imports: [],
selector: "app-home",
styleUrl: "./home.css",
templateUrl: "./home.html",
})
export class Home {
}

44
src/app/nav/nav.css Normal file
View File

@ -0,0 +1,44 @@
nav {
width: 100%;
height: 50px;
position: fixed;
background: var(--background);
color: var(--foreground);
top: 0;
}
ul {
display: flex;
justify-content: space-around;
align-items: center;
list-style: none;
margin: 0;
padding: 0;
flex-wrap: nowrap;
height: 50px;
}
li {
margin: 0 10px;
padding: 0;
}
img {
height: 50px;
}
#highlight {
background: var(--foreground);
color: var(--background);
padding: 5px 10px;
border-radius: 5px;
}
@media screen and (max-width: 650px) {
nav {
height: 100px;
}
ul {
flex-wrap: wrap;
}
}

9
src/app/nav/nav.html Normal file
View File

@ -0,0 +1,9 @@
<nav>
<ul>
<li style="height: 50px;"><a routerLink="/" style="height: 0; display: inline-block;"><img src="https://cdn.yurigpt.com/yurigpt.png" alt="YuriGPT Logo" /></a></li>
<li><a routerLink="/about">About</a></li>
<li id="highlight"><a routerLink="/comic">Start Reading</a></li>
<li><a routerLink="/characters">Characters</a></li>
<li><a routerLink="/archive">Archive</a></li>
</ul>
</nav>

18
src/app/nav/nav.ts Normal file
View File

@ -0,0 +1,18 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Component } from "@angular/core";
import { RouterModule } from "@angular/router";
@Component({
imports: [ RouterModule ],
selector: "app-nav",
styleUrl: "./nav.css",
templateUrl: "./nav.html",
})
export class Nav {
}

14
src/index.html Normal file
View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Yurigpt</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
<script src="https://cdn.nhcarrigan.com/headers/index.js"></script>
</html>

15
src/main.ts Normal file
View File

@ -0,0 +1,15 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { bootstrapApplication } from "@angular/platform-browser";
import { appConfig } from "./app/app.config.js";
import { App } from "./app/app.js";
bootstrapApplication(App, appConfig).
// eslint-disable-next-line unicorn/prefer-top-level-await -- It is an Angular thing.
catch((error: unknown) => {
console.error(error);
});

1
src/styles.css Normal file
View File

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

33
tsconfig.json Normal file
View File

@ -0,0 +1,33 @@
/* 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": {
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"isolatedModules": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "ES2022",
"module": "preserve",
"outDir": "./out-tsc/app",
"types": []
},
"include": [
"src/**/*.ts"
],
"exclude": [
"src/**/*.spec.ts"
],
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"typeCheckHostBindings": true,
"strictTemplates": true
}
}