mirror of
https://github.com/linsa-io/linsa.git
synced 2026-04-24 01:08:36 +02:00
Setup (#112)
* wip * wip * wip3 * chore: utils * feat: add command * wip * fix: key duplicate * fix: move and check * fix: use react-use instead * fix: sidebar * chore: make dynamic * chore: tablet mode * chore: fix padding * chore: link instead of inbox * fix: use dnd kit * feat: add select component * chore: use atom * refactor: remove dnd provider * feat: disabled drag when sort is not manual * search route * . * feat: accessibility * fix: search * . * . * . * fix: sidebar collapsed * ai search layout * . * . * . * . * ai responsible content * . * . * . * . * . * global topic route * global topic correct route * topic buttons * sidebar search navigation * ai * Update jazz * . * . * . * . * . * learning status * . * . * chore: content header * fix: pointer none when dragging. prevent auto click after drag end * fix: confirm * fix: prevent drag when editing * chore: remove unused fn * fix: check propagation * chore: list * chore: tweak sonner * chore: update stuff * feat: add badge * chore: close edit when create * chore: escape on manage form * refactor: remove learn path * css: responsive item * chore: separate pages and topic * reafactor: remove new-schema * feat(types): extend jazz type so it can be nullable * chore: use new types * fix: missing deps * fix: link * fix: sidebar in layout * fix: quotes * css: use medium instead semi * Actual streaming and rendering markdown response * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * chore: metadata * feat: la-editor * . * fix: editor and page * . * . * . * . * . * . * fix: remove link * chore: page sidebar * fix: remove 'replace with learning status' * fix: link * fix: link * chore: update schema * chore: use new schema * fix: instead of showing error, just do unique slug * feat: create slug * refactor apply * update package json * fix: schema personal page * chore: editor * feat: pages * fix: metadata * fix: jazz provider * feat: handling data * feat: page detail * chore: server page to id * chore: use id instead of slug * chore: update content header * chore: update link header implementation * refactor: global.css * fix: la editor use animation frame * fix: editor export ref * refactor: page detail * chore: tidy up schema * chore: adapt to new schema * fix: wrap using settimeout * fix: wrap using settimeout * . * . --------- Co-authored-by: marshennikovaolga <marshennikova@gmail.com> Co-authored-by: Nikita <github@nikiv.dev> Co-authored-by: Anselm <anselm.eickhoff@gmail.com> Co-authored-by: Damian Tarnawski <gthetarnav@gmail.com>
This commit is contained in:
34
web/lib/schema/global-link.ts
Normal file
34
web/lib/schema/global-link.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { co, CoMap, Encoders } from "jazz-tools"
|
||||
import { GlobalTopic } from "./global-topic"
|
||||
|
||||
// GlobalLinkAiSummary is high quality title, description, summary of link (generated by AI)
|
||||
export class GlobalLinkAiSummary extends CoMap {
|
||||
link = co.ref(GlobalLink) // link the summary is for
|
||||
aiModelUsed = co.string // model used to generate summary, e.g. "gpt-4o"
|
||||
title = co.string // high quality title of link
|
||||
description = co.string // high quality description of link
|
||||
summary = co.string // high quality summary of link
|
||||
// vectorContent = co.optional.vector // vector content of link, TODO: jazz needs support for this
|
||||
}
|
||||
|
||||
// GlobalLinkSummary is high quality title, description, summary of link
|
||||
export class GlobalLinkSummary extends CoMap {
|
||||
link = co.ref(GlobalLink) // link the summary is for
|
||||
title = co.string // high quality title of link
|
||||
description = co.string // high quality description of link
|
||||
summary = co.string // high quality summary of link
|
||||
}
|
||||
|
||||
// GlobalLink is link with unique URL that holds some useful metadata. Can be used to do queries like `most popular links added by users` etc.
|
||||
export class GlobalLink extends CoMap {
|
||||
url = co.string // url without the protocol (e.g. "learn-anything.xyz") (unique)
|
||||
urlTitle = co.string // title of the page (as grabbed from url itself)
|
||||
protocol = co.literal("https") // TODO: other protocols? perhaps should be `co.string` for user custom protocols?
|
||||
aiSummary = co.optional.ref(GlobalLinkAiSummary) // TODO: should probably be a list? with allowing users to vote on results so 'top' one can be shown too
|
||||
summary = co.optional.ref(GlobalLinkSummary) // the very 'best' summary of link (can be part AI generated, part human written, whatever is best)
|
||||
urlWasCreatedOnInternetAt = co.optional.encoded(Encoders.Date) // date url was created on internet at (can pass just year or year+month or year+month+day)
|
||||
globalMainTopic = co.optional.ref(GlobalTopic) // each global link can have one main topic (to closest approximation)
|
||||
public = co.optional.boolean // if true, link is accessible from global search
|
||||
processed = co.optional.encoded(Encoders.Date) // date link was processed at by LA (update metadata etc.)
|
||||
// websiteDown = co.boolean // if website is 404 or down TODO: add it later when we have service to check for dead links
|
||||
}
|
||||
21
web/lib/schema/global-topic.ts
Normal file
21
web/lib/schema/global-topic.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { co, CoList, CoMap } from "jazz-tools"
|
||||
|
||||
// GlobalTopic is topic that is accessible from `learn-anything.xyz/name`
|
||||
export class GlobalTopic extends CoMap {
|
||||
name = co.string // name of topic, used for url `learn-anything.xyz/name` (unique)
|
||||
prettyName = co.string // pretty name of topic, e.g. "React"
|
||||
aiSummary = co.optional.ref(GlobalTopicAiSummary) // high quality summary of topic (generated by AI)
|
||||
humanSummary = co.optional.string // human created summary of link (simplified, cleaner)
|
||||
topicAiSummary = co.optional.string // summary of topic (generated by AI)
|
||||
topicSummary = co.optional.string // summary of topic (generated by AI)
|
||||
public = co.optional.boolean // if true, topic is accessible from global search
|
||||
}
|
||||
|
||||
// GlobalTopicAiSummary is high quality summary of topic (generated by AI)
|
||||
export class GlobalTopicAiSummary extends CoMap {
|
||||
topic = co.ref(GlobalTopic) // topic the summary is for
|
||||
aiModelUsed = co.string // model used to generate summary, e.g. "gpt-4o"
|
||||
summary = co.string // high quality summary of topic (generated by AI)
|
||||
}
|
||||
|
||||
export class GlobalTopicLists extends CoList.Of(co.ref(GlobalTopic)) {}
|
||||
72
web/lib/schema/index.ts
Normal file
72
web/lib/schema/index.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
// Jazz schema for LA
|
||||
// if field does not have `co.optional` it is required
|
||||
// rule: list all required fields first, then optional fields
|
||||
// rule: any field that is generated by AI, should be prefixed with `ai`, e.g. `aiSummary`
|
||||
// TODO: move more fields from old edgedb schema to jazz (where it makes sense): https://github.com/learn-anything/explore/blob/main/archive/api/edgedb/dbschema/default-latest.esdl
|
||||
// TODO: all instances of (unique) should be enforced by jazz itself, it means that only one instance with that name of field can exist
|
||||
// sadly jazz does not allow enforcing that, so solutions for (unique) fields is: ignore duplicates or create a supporting look up structure (CoMap.Record from url to GlobalLink) and then I’ll let you know once the better way exists
|
||||
// open issue about it: https://github.com/gardencmp/jazz/issues/44
|
||||
// TODO: figure out how to do default values, e.g. `GlobalLink.protocol` should have default value `https` so we don't have to supply it every time in code..
|
||||
// TODO: can jazz support vector fields? e.g. `GlobalLinkAiSummary.vectorContent`, would be nice to store website content as vector for semantic search
|
||||
import { CoMap, co, Account, Group } from "jazz-tools"
|
||||
import { PersonalPageLists } from "./personal-page"
|
||||
import { PersonalLinkLists } from "./personal-link"
|
||||
import { GlobalTopicLists } from "./global-topic"
|
||||
|
||||
class UserProfile extends CoMap {
|
||||
name = co.string
|
||||
// TODO: avatar
|
||||
}
|
||||
export class UserRoot extends CoMap {
|
||||
name = co.string
|
||||
username = co.string
|
||||
website = co.string
|
||||
bio = co.string
|
||||
|
||||
personalLinks = co.ref(PersonalLinkLists)
|
||||
personalPages = co.ref(PersonalPageLists)
|
||||
|
||||
// not implemented yet
|
||||
topicsWantToLearn = co.ref(GlobalTopicLists)
|
||||
topicsLearning = co.ref(GlobalTopicLists)
|
||||
topicsLearned = co.ref(GlobalTopicLists)
|
||||
}
|
||||
|
||||
export class LaAccount extends Account {
|
||||
profile = co.ref(UserProfile)
|
||||
root = co.ref(UserRoot)
|
||||
async migrate(
|
||||
this: LaAccount,
|
||||
creationProps?: { name: string; username: string; website: string; bio: string } | undefined
|
||||
): Promise<void> {
|
||||
if (!this._refs.root && creationProps) {
|
||||
const profileGroup = Group.create({ owner: this })
|
||||
profileGroup.addMember("everyone", "reader")
|
||||
this.profile = UserProfile.create({ name: creationProps.name }, { owner: profileGroup })
|
||||
this.root = UserRoot.create(
|
||||
{
|
||||
name: creationProps.name,
|
||||
username: creationProps.username,
|
||||
website: creationProps.website,
|
||||
bio: creationProps.bio,
|
||||
|
||||
personalLinks: PersonalLinkLists.create([], { owner: this }),
|
||||
personalPages: PersonalPageLists.create([], { owner: this }),
|
||||
|
||||
// not implemented yet
|
||||
topicsWantToLearn: GlobalTopicLists.create([], { owner: this }),
|
||||
topicsLearning: GlobalTopicLists.create([], { owner: this }),
|
||||
topicsLearned: GlobalTopicLists.create([], { owner: this })
|
||||
},
|
||||
{ owner: this }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: need?
|
||||
// class ListOfGlobalTopics extends CoList.Of(co.ref(GlobalTopic)) {}
|
||||
|
||||
export * from "./global-link"
|
||||
export * from "./global-topic"
|
||||
export * from "./personal-link"
|
||||
export * from "./personal-page"
|
||||
34
web/lib/schema/personal-link.ts
Normal file
34
web/lib/schema/personal-link.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { co, CoList, CoMap } from "jazz-tools"
|
||||
import { nullable } from "../types"
|
||||
import { GlobalLink } from "./global-link"
|
||||
import { GlobalTopic } from "./global-topic"
|
||||
|
||||
export class LinkMetadata extends CoMap {
|
||||
url = co.string
|
||||
title = co.string
|
||||
favicon = co.string
|
||||
description = nullable(co.string)
|
||||
}
|
||||
|
||||
/*
|
||||
* Link is link user added, it wraps over Link and lets user add notes and other things to it,
|
||||
* (as well as set own title/description/summary if GlobalLink ones is not good enough or is lacking)
|
||||
*/
|
||||
export class PersonalLink extends CoMap {
|
||||
title = co.string
|
||||
slug = co.string
|
||||
description = nullable(co.string)
|
||||
completed = co.boolean
|
||||
sequence = co.number
|
||||
isLink = co.boolean
|
||||
meta = co.optional.ref(LinkMetadata)
|
||||
|
||||
// not yet implemented
|
||||
learningState = co.optional.literal("wantToLearn", "learning", "learned")
|
||||
notes = co.optional.string
|
||||
summary = co.optional.string
|
||||
globalLink = co.optional.ref(GlobalLink)
|
||||
topic = co.optional.ref(GlobalTopic)
|
||||
}
|
||||
|
||||
export class PersonalLinkLists extends CoList.Of(co.ref(PersonalLink)) {}
|
||||
18
web/lib/schema/personal-page.ts
Normal file
18
web/lib/schema/personal-page.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { co, CoList, CoMap } from "jazz-tools"
|
||||
import { GlobalTopic } from "./global-topic"
|
||||
|
||||
/*
|
||||
* Page, content that user can write to. Similar to Notion/Reflect page. It holds ProseMirror editor content + metadata.
|
||||
* - slug: make it unique
|
||||
* - Public Access, url should be learn-anything.xyz/@user/slug
|
||||
* - if public, certain members (can do read/write access accordingly), personal (end to end encrypted, only accessed by user)
|
||||
*/
|
||||
export class PersonalPage extends CoMap {
|
||||
title = co.string
|
||||
slug = co.string
|
||||
content = co.optional.json()
|
||||
topic = co.optional.ref(GlobalTopic)
|
||||
// backlinks = co.optional.ref() // other PersonalPages linking to this page TODO: add, think through how to do it well, efficiently
|
||||
}
|
||||
|
||||
export class PersonalPageLists extends CoList.Of(co.ref(PersonalPage)) {}
|
||||
Reference in New Issue
Block a user