Compare commits

..

10 Commits

Author SHA1 Message Date
Julien Calixte
330bc5b41d fix: scroll to stacked note on title click
- Pass didrkey (with colons) instead of classNameId to scrollToFocusedNote in StackedPublicNote, so findIndex matches the URL-stored ID
- Revert includes() to strict === in findIndex for semantic correctness
2026-03-16 18:49:21 +01:00
Julien Calixte
cfe5426421 design: same rose color 2026-03-14 13:13:22 +01:00
Julien Calixte
cf51b8b93f feat: gradient for the remanso notes 2026-03-14 12:15:18 +01:00
Julien Calixte
6c429d6d58 fix: more coherent style 2026-03-14 11:52:23 +01:00
Julien Calixte
5e69fc9d02 fix: better header 2026-03-14 11:41:59 +01:00
Julien Calixte
263f11b7e5 feat: replace back button with home button on public note list views 2026-03-14 10:25:48 +01:00
Julien Calixte
3ad5b13427 feat: add bsky logo 2026-03-10 18:04:32 +01:00
Julien Calixte
224de39cf7 fix: a better join for login 2026-03-10 17:45:21 +01:00
Julien Calixte
af49b233ef lint 2026-03-10 16:33:24 +01:00
Julien Calixte
1135afb359 lint 2026-03-10 16:15:58 +01:00
14 changed files with 115 additions and 57 deletions

View File

@@ -16,7 +16,7 @@
sizes="180x180" sizes="180x180"
/> />
<link rel="mask-icon" href="/masked-icon.png" color="#ffa4c0" /> <link rel="mask-icon" href="/masked-icon.png" color="#ffa4c0" />
<meta name="theme-color" content="#2e5c1a" /> <meta name="theme-color" content="#ffa4c0" />
<link <link
rel="stylesheet" rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css"

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import { useRouter } from "vue-router"
const router = useRouter()
const goHome = () => router.push({ name: "Home" })
</script>
<template>
<a class="btn btn-ghost btn-circle btn-lg" @click="goHome">
<img src="/favicon.png" alt="Remanso icon" />
</a>
</template>
<style>
img {
box-shadow: none;
}
</style>

View File

@@ -16,10 +16,7 @@ const getStyle = (seed: string) => {
<template> <template>
<section class="repo-list"> <section class="repo-list">
<router-link <router-link :to="{ name: 'PublicNoteListView' }" class="btn special"
:to="{ name: 'PublicNoteListView' }"
class="btn"
:style="getStyle(``)"
>Public notes</router-link >Public notes</router-link
> >
<router-link <router-link
@@ -50,6 +47,12 @@ const getStyle = (seed: string) => {
border: 0; border: 0;
width: 160px; width: 160px;
height: 90px; height: 90px;
font-size: 1.5rem;
}
.special {
background-image: linear-gradient(to left bottom, #8cd18d 0%, #fbc2f1 100%);
color: black;
} }
} }
</style> </style>

View File

@@ -1,11 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from "vue"
import { useATProtoLogin } from '@/hooks/useATProtoLogin.hook' import { useATProtoLogin } from "@/hooks/useATProtoLogin.hook"
const { handle, isLoggedIn, signIn, signOut } = useATProtoLogin() const { handle, isLoggedIn, signIn, signOut } = useATProtoLogin()
const inputHandle = ref('') withDefaults(
defineProps<{
withSignOut?: boolean
}>(),
{
withSignOut: true,
},
)
const inputHandle = ref("")
const onSignIn = () => { const onSignIn = () => {
if (inputHandle.value) { if (inputHandle.value) {
@@ -15,18 +24,42 @@ const onSignIn = () => {
</script> </script>
<template> <template>
<div v-if="isLoggedIn" class="sign-in-atproto"> <div v-if="isLoggedIn" class="sign-in-atproto is-signed-in">
<span>{{ handle }}</span> <span>{{ handle }}</span>
<button class="btn btn-sm" @click="signOut">Sign out</button> <button class="btn btn-sm" @click="signOut" v-if="withSignOut">
Sign out
</button>
</div> </div>
<div v-else class="sign-in-atproto"> <div v-else class="sign-in-atproto join">
<input <input
v-model="inputHandle" v-model="inputHandle"
class="input input-sm" class="input input-sm join-item"
type="text" type="text"
placeholder="yourhandle.bsky.social" placeholder="alice.bsky.social"
@keyup.enter="onSignIn" @keyup.enter="onSignIn"
/> />
<button class="btn btn-sm" @click="onSignIn">Sign in with Bluesky</button> <button class="btn input-sm join-item" @click="onSignIn">
Sign in with
<svg
width="20"
height="20"
viewBox="0 0 600 530"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m135.72 44.03c66.496 49.921 138.02 151.14 164.28 205.46 26.262-54.316 97.782-155.54 164.28-205.46 47.98-36.021 125.72-63.892 125.72 24.795 0 17.712-10.155 148.79-16.111 170.07-20.703 73.984-96.144 92.854-163.25 81.433 117.3 19.964 147.14 86.092 82.697 152.22-122.39 125.59-175.91-31.511-189.63-71.766-2.514-7.3797-3.6904-10.832-3.7077-7.8964-0.0174-2.9357-1.1937 0.51669-3.7077 7.8964-13.714 40.255-67.233 197.36-189.63 71.766-64.444-66.128-34.605-132.26 82.697-152.22-67.108 11.421-142.55-7.4491-163.25-81.433-5.9562-21.282-16.111-152.36-16.111-170.07 0-88.687 77.742-60.816 125.72-24.795z"
fill="#1185fe"
/>
</svg>
</button>
</div> </div>
</template> </template>
<style scoped>
.is-signed-in {
display: flex;
gap: 1rem;
align-items: center;
}
</style>

View File

@@ -1,20 +1,20 @@
<script lang="ts" setup> <script lang="ts" setup>
const GITHUB_URL = 'https://github.com/login/oauth/authorize' const GITHUB_URL = "https://github.com/login/oauth/authorize"
const CLIENT_ID = 'Iv1.12dc43d013ce3623' const CLIENT_ID = "Iv1.12dc43d013ce3623"
const SCOPE = 'repo%20workflow' const SCOPE = "repo%20workflow"
const REDIRECT_URI = window.location.origin const REDIRECT_URI = window.location.origin
const url = new URL(GITHUB_URL) const url = new URL(GITHUB_URL)
url.searchParams.set('client_id', CLIENT_ID) url.searchParams.set("client_id", CLIENT_ID)
url.searchParams.set('scope', SCOPE) url.searchParams.set("scope", SCOPE)
url.searchParams.set('redirect_uri', REDIRECT_URI) url.searchParams.set("redirect_uri", REDIRECT_URI)
const href = url.toString() const href = url.toString()
</script> </script>
<template> <template>
<a :href="href" class="sign-in-github btn btn-primary"> <a :href="href" class="sign-in-github btn btn-sm btn-primary">
Sign in with Sign in with
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -85,7 +85,7 @@ watch(
> >
<a <a
class="title-stacked-note-link" class="title-stacked-note-link"
@click.prevent="scrollToFocusedNote(classNameId)" @click.prevent="scrollToFocusedNote(didrkey)"
> >
<div <div
class="title-stacked-note breadcrumbs text-sm" class="title-stacked-note breadcrumbs text-sm"

View File

@@ -23,17 +23,9 @@ const { userInput, repoInput, submit } = useForm()
<last-visited /> <last-visited />
<div class="get-started"> <div class="get-started">
<sign-in-atproto :with-sign-out="false" />
<sign-in-github /> <sign-in-github />
<sign-in-atproto /> <router-link v-if="isLogged" :to="{ name: 'RepoList' }" class="btn btn-sm"
<router-link
:to="{
name: 'FluxNoteView',
params: { user: 'remanso-space', repo: 'getting-started' },
}"
class="btn"
>Get started</router-link
>
<router-link v-if="isLogged" :to="{ name: 'RepoList' }" class="btn"
>Manage your repos</router-link >Manage your repos</router-link
> >
</div> </div>
@@ -80,6 +72,14 @@ const { userInput, repoInput, submit } = useForm()
<a href="https://apoena.dev" target="_blank" rel="noopener noreferrer" <a href="https://apoena.dev" target="_blank" rel="noopener noreferrer"
>apoena</a >apoena</a
> >
<router-link
:to="{
name: 'FluxNoteView',
params: { user: 'remanso-space', repo: 'getting-started' },
}"
class="btn btn-sm"
>Get started</router-link
>
</footer> </footer>
</div> </div>
</template> </template>
@@ -126,6 +126,7 @@ h1 {
footer { footer {
display: flex; display: flex;
gap: 1rem; gap: 1rem;
align-items: center;
img { img {
vertical-align: middle; vertical-align: middle;

View File

@@ -25,7 +25,7 @@ export const useRouteQueryStackedNotes = () => {
notes: string[] = stackedNotes.value, notes: string[] = stackedNotes.value,
) => { ) => {
nextTick(() => { nextTick(() => {
const index = noteId ? notes.findIndex((nid) => nid.includes(noteId)) : 0 const index = noteId ? notes.findIndex((nid) => nid === noteId) : 0
if (isMobile.value) { if (isMobile.value) {
if (noteId) { if (noteId) {

View File

@@ -1,9 +1,12 @@
import { BrowserOAuthClient, buildLoopbackClientId } from '@atproto/oauth-client-browser' import {
BrowserOAuthClient,
buildLoopbackClientId,
} from "@atproto/oauth-client-browser"
const getClientId = () => const getClientId = () =>
import.meta.env.DEV import.meta.env.DEV
? buildLoopbackClientId(new URL(window.location.origin)) ? buildLoopbackClientId(new URL(window.location.origin))
: 'https://remanso.space/client-metadata.json' : "https://remanso.space/client-metadata.json"
let clientPromise: Promise<BrowserOAuthClient> | null = null let clientPromise: Promise<BrowserOAuthClient> | null = null
@@ -11,7 +14,7 @@ export const getOAuthClient = (): Promise<BrowserOAuthClient> => {
if (!clientPromise) { if (!clientPromise) {
clientPromise = BrowserOAuthClient.load({ clientPromise = BrowserOAuthClient.load({
clientId: getClientId(), clientId: getClientId(),
handleResolver: 'https://bsky.social', handleResolver: "https://bsky.social",
}) })
} }
return clientPromise return clientPromise

View File

@@ -4,7 +4,7 @@
@import url("https://fonts.googleapis.com/css2?display=swap&family=Courier+Prime&family=Libertinus+Serif"); @import url("https://fonts.googleapis.com/css2?display=swap&family=Courier+Prime&family=Libertinus+Serif");
:root { :root {
--primary-color: #2e5c1a; --primary-color: #ffa4c0;
--font-family: "Libertinus Serif", serif; --font-family: "Libertinus Serif", serif;
--font-size: 13pt; --font-size: 13pt;
--font-color: #4a4a4a; --font-color: #4a4a4a;

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import BackButton from "@/components/BackButton.vue" import HomeButton from "@/components/HomeButton.vue"
import PublicNoteList from "@/components/PublicNoteList.vue" import PublicNoteList from "@/components/PublicNoteList.vue"
import { usePublicNoteList } from "@/hooks/usePublicNoteList.hook" import { usePublicNoteList } from "@/hooks/usePublicNoteList.hook"
import { getAuthor } from "@/modules/atproto/getAuthor" import { getAuthor } from "@/modules/atproto/getAuthor"
@@ -17,7 +17,7 @@ const author = computedAsync(async () => getAuthor(did.value))
<template> <template>
<main class="public-note-list-view"> <main class="public-note-list-view">
<div class="header"> <div class="header">
<back-button class="back-button" :fallback="{ name: 'Home' }" /> <home-button class="back-button" />
<h1>{{ author?.handle ?? did }}</h1> <h1>{{ author?.handle ?? did }}</h1>
</div> </div>
<div v-if="isLoading"></div> <div v-if="isLoading"></div>
@@ -59,9 +59,7 @@ const author = computedAsync(async () => getAuthor(did.value))
} }
.back-button { .back-button {
display: flex; position: absolute;
gap: 0.5rem;
align-items: center;
} }
@media screen and (min-width: 769px) { @media screen and (min-width: 769px) {

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import BackButton from "@/components/BackButton.vue" import HomeButton from "@/components/HomeButton.vue"
import PublicNoteList from "@/components/PublicNoteList.vue" import PublicNoteList from "@/components/PublicNoteList.vue"
import SignInAtproto from "@/components/SignInAtproto.vue" import SignInAtproto from "@/components/SignInAtproto.vue"
import { useATProtoLogin } from "@/hooks/useATProtoLogin.hook" import { useATProtoLogin } from "@/hooks/useATProtoLogin.hook"
@@ -23,7 +23,7 @@ const tab = computed<"all" | "following">({
}), }),
}) })
const followingEnabled = computed(() => tab.value === 'following') const followingEnabled = computed(() => tab.value === "following")
const all = usePublicNoteList() const all = usePublicNoteList()
const following = useFollowingNoteList(follows, followingEnabled) const following = useFollowingNoteList(follows, followingEnabled)
@@ -32,9 +32,7 @@ const following = useFollowingNoteList(follows, followingEnabled)
<template> <template>
<main class="public-note-list-view"> <main class="public-note-list-view">
<div class="header"> <div class="header">
<back-button class="back-button" :fallback="{ name: 'Home' }" /> <home-button class="back-button" />
<h1><img src="/favicon.png" alt="Remanso icon" /></h1>
<sign-in-atproto />
</div> </div>
<div v-if="isLoggedIn" role="tablist" class="tabs tabs-border"> <div v-if="isLoggedIn" role="tablist" class="tabs tabs-border">
@@ -52,6 +50,7 @@ const following = useFollowingNoteList(follows, followingEnabled)
@click="tab = 'following'" @click="tab = 'following'"
>Following</a >Following</a
> >
<sign-in-atproto class="handle" />
</div> </div>
<PublicNoteList <PublicNoteList
@@ -101,12 +100,14 @@ const following = useFollowingNoteList(follows, followingEnabled)
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
h1 { .tabs {
img { position: relative;
width: 64px; }
height: 64px;
box-shadow: none; .handle {
} position: absolute;
right: 0;
align-self: center;
} }
.public-note-list-view { .public-note-list-view {
@@ -117,7 +118,7 @@ h1 {
padding-right: 1rem; padding-right: 1rem;
.header { .header {
margin-top: 1rem; margin: 0.5rem auto 0;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;

View File

@@ -128,7 +128,6 @@ watch(
:fallback="{ name: 'PublicNoteListByDidView', params: { did } }" :fallback="{ name: 'PublicNoteListByDidView', params: { did } }"
:prefer-fallback="false" :prefer-fallback="false"
/> />
<theme-swap />
<span <span
class="badge badge-author badge-soft badge-accent" class="badge badge-author badge-soft badge-accent"
@@ -150,7 +149,9 @@ watch(
</template> </template>
</span> </span>
<div class="badge skeleton h-4 w-50" v-else></div> <div class="badge skeleton h-4 w-50" v-else></div>
<theme-swap />
</div> </div>
<div class="meta"></div>
<div class="repo-title-breadcrumb"> <div class="repo-title-breadcrumb">
<a <a
class="title-stacked-note-link" class="title-stacked-note-link"

View File

@@ -27,7 +27,7 @@ export default defineConfig(({ command }) => {
short_name: "Remanso", short_name: "Remanso",
description: "Note taking & sharing app", description: "Note taking & sharing app",
background_color: "#ffa4c0", background_color: "#ffa4c0",
theme_color: "#2e5c1a", theme_color: "#ffa4c0",
icons: [ icons: [
{ {
src: "pwa-64x64.png", src: "pwa-64x64.png",
@@ -78,7 +78,7 @@ export default defineConfig(({ command }) => {
global: {}, global: {},
} }
config.server = { config.server = {
host: '127.0.0.1', host: "127.0.0.1",
} }
} }