Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,47 @@ Please report bugs in the github issues.
## Build Setup
We use Node.js 22 and pnpm 8.15.8. The project also needs Python 3.11 because some legacy build dependencies compile native modules.

### With Nix (recommended)

A Nix flake is provided with all dependencies pre-configured:

```bash
# Enter development shell
$ nix develop

# Install dependencies
$ pnpm install --frozen-lockfile
```

### Standard setup

```bash
# install dependencies
$ pnpm install

# serve with hot reload at localhost:3000
$ pnpm dev

# build for production and launch server
# build for production and launch server (recommended for low-memory environments)
$ pnpm build
$ pnpm start

# generate static project
$ pnpm generate
```

### Exposing via Tailscale

If running on a remote server or VM, expose the instance to your tailnet:

```bash
# For production preview (HTTP on port 3000):
$ tailscale serve --bg 3000

# For dev server (HTTPS on port 3000):
$ tailscale serve --bg https+insecure://localhost:3000
```

Search for prettier and eslint in pycharm to set it up on saving a file.
You can also add .vue there for running stuff on Vue files as well.

Expand Down
8 changes: 8 additions & 0 deletions components/GlobalFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export default {
// name: "whatsapp-to-pdf",
// text: "Create a PDF from your WhatsApp chat",
// },
{
name: "court-evidence",
text: "pageNameCourt",
},
{
name: "proof-of-relationship",
text: "pageNameRelationship",
},
{
name: "switch-from-whatsapp-to-signal",
text: "pageNameSignal",
Expand Down
40 changes: 40 additions & 0 deletions components/landing/LandingButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<NuxtLink :to="to" class="landing-btn" @click="$emit('click')">
<slot />
</NuxtLink>
</template>

<script>
export default {
name: "LandingButton",
props: {
to: { type: [String, Object], required: true },
},
emits: ["click"],
};
</script>

<style lang="scss" scoped>
.landing-btn {
display: inline-block;
padding: 0.9em 2.2em;
border-radius: 999px;
background: $c-blue-accent;
color: #ffffff !important;
font-size: clamp(1rem, 1.5vw, 1.2rem);
font-weight: 600;
text-decoration: none;
transition: transform 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 10px 30px rgba(33, 166, 141, 0.35);

&:hover {
background: $c-blue-accent-light;
transform: translateY(-1px);
box-shadow: 0 14px 34px rgba(33, 166, 141, 0.45);
}

&:active {
transform: translateY(0);
}
}
</style>
89 changes: 89 additions & 0 deletions components/landing/LandingCards.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<template>
<div class="landing-cards">
<component
:is="item.to ? 'NuxtLink' : 'div'"
v-for="item in items"
:key="item.title"
:to="item.to"
class="landing-card"
:class="{ 'landing-card--link': item.to }"
>
<v-icon v-if="item.icon" class="landing-card__icon" size="32">
{{ item.icon }}
</v-icon>
<h3 class="landing-card__title">{{ item.title }}</h3>
<p class="landing-card__text">{{ item.text }}</p>
<span v-if="item.to" class="landing-card__more">
{{ item.linkText }} <v-icon size="16">mdi-arrow-right</v-icon>
</span>
</component>
</div>
</template>

<script>
export default {
name: "LandingCards",
props: {
// [{ icon, title, text, to?, linkText? }]
items: { type: Array, required: true },
},
};
</script>

<style lang="scss" scoped>
.landing-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
gap: 1.2rem;
margin-top: clamp(2.2rem, 5vw, 3.6rem);
text-align: left;
}

.landing-card {
background: var(--landing-card-bg, #ffffff);
color: var(--landing-card-fg, #1d1d1f);
border-radius: 20px;
padding: 1.8rem 1.6rem;
box-shadow: var(--landing-card-shadow, none);
text-decoration: none;
display: flex;
flex-direction: column;
}

.landing-card--link {
transition: transform 0.25s ease;

&:hover {
transform: translateY(-4px);
}
}

.landing-card__icon {
color: $c-blue-accent;
margin-bottom: 1rem;
}

.landing-card__title {
font-size: 1.15rem;
font-weight: 700;
line-height: 1.3;
margin: 0 0 0.6rem;
}

.landing-card__text {
font-size: 1rem;
line-height: 1.55;
color: var(--landing-card-muted, rgba(29, 29, 31, 0.68));
margin: 0;
}

.landing-card__more {
margin-top: auto;
padding-top: 1rem;
font-weight: 600;
color: $c-blue-accent;
display: inline-flex;
align-items: center;
gap: 0.3em;
}
</style>
52 changes: 52 additions & 0 deletions components/landing/LandingCta.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<template>
<LandingSection theme="dark">
<h2 class="landing-cta__title">{{ title }}</h2>
<div class="landing-cta__actions">
<LandingButton :to="ctaTo">{{ ctaText }}</LandingButton>
</div>
<p v-if="note" class="landing-cta__note">{{ note }}</p>
<p v-if="disclaimer" class="landing-cta__disclaimer">{{ disclaimer }}</p>
</LandingSection>
</template>

<script>
export default {
name: "LandingCta",
props: {
title: { type: String, required: true },
ctaText: { type: String, required: true },
ctaTo: { type: [String, Object], default: "/" },
note: { type: String, default: "" },
disclaimer: { type: String, default: "" },
},
};
</script>

<style lang="scss" scoped>
.landing-cta__title {
font-size: clamp(2.2rem, 6vw, 4rem);
font-weight: 700;
line-height: 1.08;
letter-spacing: -0.02em;
max-width: 20ch;
margin: 0 auto;
}

.landing-cta__actions {
margin-top: 2.2rem;
}

.landing-cta__note {
margin-top: 1.2rem;
font-size: 0.95rem;
color: rgba(245, 245, 247, 0.55);
}

.landing-cta__disclaimer {
margin: clamp(2.5rem, 6vw, 4rem) auto 0;
max-width: 40rem;
font-size: 0.8rem;
line-height: 1.5;
color: rgba(245, 245, 247, 0.4);
}
</style>
Loading
Loading