michael / michael/svedit

Hyperlinks and linking in general

Open
#49 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
675
Forks
18
Avg merge
5h 11m
Merged PRs (30d)
1

Description

My first intuition was that for internal links we should just use node refs. However, that comes at the expense that node refs will be resolved/expanded when you load a document. Which means if page_1 links to page_3 the entire page 3 would get loaded, and if page 3 contains more references to other documents those will be resolved as well.

I think it's not practical, and we should instead come up with a generic concept 'url' or 'hyperlink' that will not be resolved within the graph directly. So instead of being full "embeds" those links just point to a url (internal or external). I'd introduce a new property type for nodes called 'url' which we can schema-check for being a valid URL.

In a later step we can precompute/derive url previews, where on the server we resolve urls to show title+description+image of the target if possible. This data we can provide to the client to render previews (like before you click the link you could already see what you will expect there, much like Wikipedia does it). However, it's important that this info is not modelled in our graph and that those are not part of the source-of-truth. It's generated (app-specific) information, that can be attached separately.

Workflows for creating a link (early ideas - WIP):

Node-level Links (a whole node links to something)

  • Select the whole node and press the "link" button (or cmd+k)
  • A link selector pops up where you can search all the pages by just typing a search string, or you enter a full URL (e.g. an external link manually)
  • Now you have the whole node linked (which should be visually indicated in a subtle way, so as a reader you know this element is clickable)

Considerations:

  • Not all blocks should be linkable, just everything that looks block-ish visually makes sense imo. E.g. a grid item is perfect; but a list item or a heading not so much (there you can use inline links). Also blocks that have links inside should not be linkable, e.g. if you linked a story node you'd have two links nested links.
  • When you link an "empty node" (e.g. a grid item that does not have a title or image yet) then it can be automatically filled up with preview infos. So if you link to page 423 "Project X" then this label will be automatically added to the grid item, as well as the preview image of that page. If you already have a title and an image when you create the link, those won't be touched and only the link is added.
  • We could potentially show the link tool also when multiple nodes are selected and then add the link to all those nodes. However, I don't think that for instance in a project grid you'd ever link the same

Inline links (link a range of a text, the classical hyperlink)

I think those are pretty clear how they work (much like Google Docs). We just need to be provide a consistent add/edit/delete workflow.

Button links (a button e.g. inside a story block)

This one i'm the least sure about. I think there's basically two direction we could go:

Fixed buttons inside a node (e.g. Story)

My intuitively favored model for button links in a story block would look like this.

{
  type: 'story',
  title: 'Story title',
  description: 'Story descripton',
  primary_button: 'button_123',
  secondary_button: null,
},
{
  type: 'button',
  id: 'button_122',
  label: 'Buy now',
  href: '/FyBYeKrPKqQrjprWJBnWGmt'
}

primary_button and and secondary_button have property type 'node' with node_types: ['button']. So embedding single nodes at a fixed place is already possible. However, you currently can not select those nodes as a whole and delete/reinsert them atm. So we'd need to introduce some Svedit infrastructure to handle OptionalNodes. E.g. like this inside Story.svelte:

<div class="buttons">
  <OptionalNode path={[...path, 'primary_button']} />
  <OptionalNode path={[...path, 'secondary_button']} />
</div>

This renders the button node when there's a reference, when the property is null, it should render a selectable placeholder similar to CustomProperty. When you select the placeholder and press ENTER the button appears and you can edit it and link it.

However in addition to edit the button label it should also be easily possible to select the whole button (then the link tool shows up and with backspace you can remove the button, leaving the placeholder there)

Buttons inside a container

We could also put the buttons into a node_array and then have the classic container semantics.

{
  type: 'story',
  title: 'Story title',
  description: 'Story descripton',
  buttons: ['button_122', 'button_123']
},
{
  type: 'button',
  id: 'button_122',
  label: 'Buy now',
  button_style: 'primary',
  href: '/FyBYeKrPKqQrjprWJBnWGmt'
}

Drawback here is that we can't control how many button someone inserts, so users may break their layouts. We can also not make styling assumptions like that the primary button should be styled differently than the secondary button, though the Button could select a button_style, then it's up to the user, I guess that would be fine. You can delete buttons with the node cursor and backspace, and you can insert a new one with the node cursor and ENTER. When all nodes are deleted only a small container cursor remains.

Since we have all the infrastructure in place we could try out buttons a container in the demo app and see how it feels.

General considerations:

  • Much like we have kind 'text' (has a property content) we can have kind 'linkable' (has a property href) so we could implement the linking tool in a generic way.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the existing Svedit infrastructure referenced in the issue, especially Story.svelte, OptionalNode, and CustomProperty, then inspect the demo app's handling of buttons and node references. The issue presents several alternative linking models rather than a fixed implementation target; done would require an agreed link representation and workflow before implementation can be scoped.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
design, frontend, web-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.