Skip to content

New Heading component - #766

Merged
timea-solid merged 40 commits into
stagingfrom
headingComponent
Oct 14, 2026
Merged

timea-solid merged 40 commits into
stagingfrom
headingComponent

Conversation

@timea-solid

Copy link
Copy Markdown
Member

This component is more or less a code copy from profile-pane. Of course we added the right logic when to display in solid-panes and the transitions.

…s. This is the repository profile.pane. I want to create a web component out of it much like Header.ts in solid-panes. I want this new component to be in solid-panes under components and it doe snot need any of the edit capabilities that profile-pane offers.

Please cerate one for me in solid-panes components.
Co-authored-by: GitHub Copilot (auto) <copilot@github.com>
…lay it on top of the navbar component but only when the profile or social pane is displayed under.

Prompt: I do not want the new profile heading to be part of teh navbar nor the header. What other options do I have to integrate it?
Prompt: awesome.
Actually... I want to be able to edit the information exactly like in profile-pane
Co-authored-by: GitHub Copilot (auto) <copilot@github.com>
…you used svg, I would like to use lucide icons exactly like in profile-pane, the same icons. can you replace them?

Co-authored-by: GitHub Copilot (auto) <copilot@github.com>
…ane is displayed, exclusively. Not on social or anyother if there will be in the future another.

Co-authored-by: GitHub Copilot (auto) <copilot@github.com>
… the heading to be displayed on all menu items in teh navbar.

Co-authored-by: GitHub Copilot (auto) <copilot@github.com>
…section. When i edit that i want the profile heading to be refreshed.

Prompt: do the same for the More contacts section of profile.pane which need to be displayed in the profileHeading new component
Co-authored-by: GitHub Copilot (auto) <copilot@github.com>
…when shown on other panes than profile. The profile picture should be half the size, only show the name and job title. Do not show pronouns, date of birth, location or DOB or anything else.

Make it smaller through CSS and build the logic when to change.
Also I would like to attempt an annitation effect: when I navigate from profile and a full profile heading to social pane, to a smaller heading i would like to see an animation transitioning to smaller. Can you do this?
Prompt: Great job.
When I move to a pane where the heading should not be displayed at all, I also want a transition that makes it dissapear. Either from profile-pane to nothing or from social pane to nothing.
Prompt: Great. Now also do a reverse transition when going from a pane that has no heading to one that has.
Co-authored-by: GitHub Copilot (auto) <copilot@github.com>
@timea-solid timea-solid self-assigned this Oct 7, 2026
@timea-solid timea-solid moved this to In progress in SolidOS NLNet UI Oct 7, 2026
@timea-solid timea-solid linked an issue Oct 7, 2026 that may be closed by this pull request

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Data integrity, upload safety, phone parsing, and accessibility issues remain unresolved.

8 open findings
What changed in this PR

Adds a responsive profile heading with compact navigation mode, owner editing, RDF persistence, and animated transitions.

Changes:

  • Adds the profile heading component, data presentation, and navigation integration.
  • Adds an owner-facing editor with dialogs, validation, image uploads, and RDF mutations.
  • Adds comprehensive heading tests and updates profile-pane integration.
File Description
vite.config.mts Improves watch-mode output handling.
test/​unit/​mainPage/​profileHeadingEditor.test.ts Tests editor dialog behavior.
test/​unit/​mainPage/​profileHeadingData.test.ts Tests RDF presentation and viewer modes.
test/​unit/​mainPage/​profileHeading.test.ts Tests rendering, navigation, refreshes, and animations.
src/​mainPage/​profileHeading.ts Controls heading lifecycle and transitions.
src/​mainPage/​profileHeading.css Adds mount spacing.
src/​mainPage/​navbar.ts Adds profile metadata to navigation items.
src/​mainPage/​index.ts Initializes and refreshes the heading.
src/​icons/​signUp.svg Removes the legacy sign-up icon.
src/​icons/​signOut.svg Removes the legacy sign-out icon.
src/​icons/​downArrow.svg Removes the legacy arrow icon.
src/​icons/​comment.svg Removes the legacy comment icon.
src/​icons/​clock.svg Removes the legacy clock icon.
src/​components/​profile-heading/​profileHeadingData.ts Presents profile RDF data.
src/​components/​profile-heading/​ProfileHeading.ts Implements the heading component.
src/​components/​profile-heading/​ProfileHeading.styles.css Styles responsive and compact layouts.
src/​components/​profile-heading/​index.ts Exports the component API.
src/​components/​profile-heading/​editor/​utils/​errorDisplay.ts Formats dialog errors.
src/​components/​profile-heading/​editor/​ui/​spinner.ts Adds the saving indicator.
src/​components/​profile-heading/​editor/​ui/​dialog.ts Implements shared modal behavior.
src/​components/​profile-heading/​editor/​ui/​dialog.responsive.css Adds responsive modal styles.
src/​components/​profile-heading/​editor/​ui/​dialog.css Styles shared dialogs.
src/​components/​profile-heading/​editor/​types.ts Defines editor modes.
src/​components/​profile-heading/​editor/​textUtils.ts Adds text conversion and sanitization.
src/​components/​profile-heading/​editor/​texts/​qrCodeTexts.ts Adds QR-related text.
src/​components/​profile-heading/​editor/​texts/​profileTexts.ts Adds profile section text.
src/​components/​profile-heading/​editor/​texts/​messageTexts.ts Adds status and error messages.
src/​components/​profile-heading/​editor/​texts/​dialogTexts.ts Adds dialog labels.
src/​components/​profile-heading/​editor/​texts/​buttonTexts.ts Adds button labels.
src/​components/​profile-heading/​editor/​texts.ts Re-exports editor text.
src/​components/​profile-heading/​editor/​styles/​EditDialogs.responsive.css Styles mobile editor layouts.
src/​components/​profile-heading/​editor/​styles/​EditDialogs.css Styles editor forms.
src/​components/​profile-heading/​editor/​sections/​shared/​types.ts Defines mutation interfaces.
src/​components/​profile-heading/​editor/​sections/​shared/​sanitizeUtils.ts Sanitizes form values.
src/​components/​profile-heading/​editor/​sections/​shared/​rowState.ts Tracks editable row state.
src/​components/​profile-heading/​editor/​sections/​shared/​rdfMutationHelpers.ts Implements RDF update transports.
src/​components/​profile-heading/​editor/​sections/​shared/​phoneCountries.ts Supplies phone prefixes and parsing.
src/​components/​profile-heading/​editor/​sections/​shared/​idNodeFactory.ts Creates RDF node identifiers.
src/​components/​profile-heading/​editor/​sections/​shared/​contactTypeUtils.ts Normalizes contact types.
src/​components/​profile-heading/​editor/​sections/​heading/​types.ts Defines heading data structures.
src/​components/​profile-heading/​editor/​sections/​heading/​mutations.ts Persists heading changes.
src/​components/​profile-heading/​editor/​sections/​heading/​imagePreview.ts Renders image previews.
src/​components/​profile-heading/​editor/​sections/​heading/​imageHelpers.ts Handles image retrieval and uploads.
src/​components/​profile-heading/​editor/​sections/​heading/​HeadingEditDialog.ts Implements the heading editor.
src/​components/​profile-heading/​editor/​sections/​heading/​dateHelpers.ts Converts profile dates.
src/​components/​profile-heading/​editor/​sections/​contactInfo/​types.ts Defines contact data types.
src/​components/​profile-heading/​editor/​index.ts Exports editor APIs.
src/​components/​navbar/​Navbar.ts Extends navbar item metadata.
src/​components/​navbar/​Navbar.styles.css Adds navbar hover styling.
README.md Documents heading behavior.
package.json Updates the profile-pane dependency.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/components/profile-heading/editor/sections/heading/mutations.ts
Comment thread src/components/profile-heading/ProfileHeading.styles.css Outdated
Comment thread src/components/profile-heading/editor/sections/shared/phoneCountries.ts Outdated
Comment thread src/components/profile-heading/editor/sections/shared/sanitizeUtils.ts Outdated
Comment thread src/components/profile-heading/editor/ui/dialog.ts
Comment thread src/components/profile-heading/profileHeadingData.ts
accessor profileData: ProfileHeadingData | undefined = undefined

@property({ attribute: false })
accessor editContext: ProfileHeadingEditContext | undefined = undefined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could become stale because it is not updated on profile-pane-saved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what you mean here..

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You basically need to do something like this or call refreshProfileHeading(true)
the following code is better because it works automatically
That fully reloads the profile and reconstructs the edit context. It is correct, but more expensive and causes more navigation/rendering work.

if (heading && event.detail.profileData) {
  refreshRequest++
  loadingPaneKey = ''

  const profileData = event.detail.profileData
  heading.profileData = profileData

  if (heading.editContext) {
    heading.editContext = {
      ...heading.editContext,
      profileData
    }
  }

  return
}

there are 2 things one because the editContext isn't refreshed when you reopen the dialog you could see old data. Also in Lit you can't mutate because then it doesn't pick up the change you have to replace.

Comment thread src/components/profile-heading/profileHeadingData.ts Outdated
import type { ProfileDetails } from './editor/sections/heading/types'
import type { ViewerMode } from './profileHeadingData'
import { addFriend, checkFriend, friendTexts } from './friendActions'
import { formatDisplayError } from './editor/utils/errorDisplay'

@SharonStrats SharonStrats Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is some feedback I received from ai

  1. Invalidate pending friend requests when the component disconnects

disconnectedCallback() currently clears the feedback timer, but pending
checkFriend() and addFriend() promises can still resolve after the
component has been removed. Their continuations can update the detached
component and create a new feedback timer after disconnection.

Please invalidate the pending request in disconnectedCallback() (for example
by incrementing friendRequest) and add a regression test covering removal
while a friend request is still pending.

  1. Recheck friend state when the logged-in identity changes

The friend lookup is triggered when friendSubject changes. If the current
viewer changes while the subject remains the same, the component can retain
the previous viewer's friend state or error.

Please either make the current viewer identity part of the friend-check
dependencies or explicitly reset and recheck the friend state after login,
logout, or session changes.

  1. Consolidate the overlapping profile data types

ProfileHeadingData and ProfileDetails contain overlapping profile fields,
while ProfileHeadingPoint.valueNode is typed as unknown. This creates
parallel data models between the visible heading and the edit dialog.

Please consider introducing one shared presentation type and using the
appropriate rdflib Node type instead of unknown, so the heading and
editor cannot gradually diverge.

  1. Reset image fallback state when the image source changes

failedImageSrc is retained for the lifetime of the component. If an image
source fails and the profile later changes back to that source, the component
will immediately use the fallback without retrying.

Please reset the failed-image state when profileData.imageSrc changes, or
document that failed sources are intentionally not retried.

  1. Make the friend action's accessibility label reflect its state

The friend action wrapper always uses an aria-label describing add-friend
actions, even when the relationship is already present or an error is being
shown.

Please use a state-neutral label such as "Friend relationship actions", or
provide state-specific accessible text.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed

}
}

export function presentProfile (subject: NamedNode, profileStore: LiveStore = store): ProfileDetails {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we really need a few other things in the solid-ui-provider like the store and browser context these were mentioned by Noel in a previous review. When I was tackling the file explorer refactor I can see where this woudl be super helpful. The subject I think belongs in teh fileexplorer context/provider.

I think we should do this soon so that we can use them as we are redoing all the other panes. I was thinking of doing this next after teh sharing dialog but before the tabel to div stuff?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this needs to be thought trough and discussed a bit. I make a ticket. SolidOS/solidos#369

Comment thread src/mainPage/header.ts Outdated
}

// The header outlives logins, so the viewer is resolved when an item is chosen.
async function currentViewer () {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this shoudl also live with the viewer mode stuff

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consolidated this.

Comment thread src/mainPage/header.ts
@@ -32,43 +32,51 @@ export async function createHeader (outliner: OutlineManager) {
}

@SharonStrats SharonStrats Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feedback from ai

  1. Refresh storage menu items after login or account switching

The storage menu items are computed once from authn.currentUser() and their
callbacks retain those original pane objects. If the app starts logged out,
storage panes never appear after login. If the account changes, the previous
user's storage entries remain and can still be selected as long as another user
is logged in.

Please refresh or rebuild the storage menu on authentication changes, or
resolve the current viewer's storage panes when the menu is opened or selected.
Please add tests for login after header creation and switching between two
users.

  1. Handle authentication failures in account-menu actions

currentViewer() can reject through authn.checkUser(), but the menu handlers
hide the navbar before awaiting it and do not handle failures locally. A failed
session check could therefore leave the navbar hidden and produce an unhandled
rejection.

Please catch authentication or session errors, restore the navbar or show an
error state, and add a test for a rejected checkUser() call.

  1. Clarify the showPane() type

showPane() is typed using the return type of
getProfilePaneFromURI(), but it is also used for social and folder panes.
Please use a shared pane type or a more general PaneDefinition | null type so
the function's intended inputs are explicit.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these will be addressed in the navigation refactoring PR. (will follow)

Comment thread src/mainPage/index.ts
@@ -8,9 +8,10 @@ import type { RenderEnvironment } from 'pane-registry'
import * as paneRegistry from 'pane-registry'
import { getOutliner, OutlineManager } from '../index'
import { createHeader } from './header'

@SharonStrats SharonStrats Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from ai

  1. Include viewport changes in the render-environment signature

renderEnvSignature() currently includes only layout, theme, and
inputMode. It omits the environment's viewport dimensions, even though the
render environment includes viewport.

If the viewport changes without one of those three fields changing,
refreshUI() will skip the rerender. Responsive panes may therefore retain
the layout from the previous viewport.

Please either include the relevant viewport dimensions in the signature or
document why viewport changes are handled entirely by the individual panes.
Please add a test that changes the viewport while keeping layout, theme, and
input mode unchanged.

  1. Ensure the profile heading is positioned correctly during initial setup

initMainPage() calls refreshProfileHeading() before createNavbar().
When refreshProfileHeading() creates the mount point, the navbar does not
exist yet, so ensureMountPoint() appends the profile-heading mount to
MainContent. createNavbar() then inserts the navbar as the first child,
leaving the profile heading below the navbar instead of in the insertion
position selected by ensureMountPoint().

Please create the navbar before the initial profile-heading refresh, or
re-run/reposition the heading after the navbar has been created. Add an
assertion in the initial-page test for the expected order of the profile
heading mount, navbar, and pane content.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thesea are also addressed in the refactoring to navigation controller

@@ -0,0 +1,308 @@
import { authn, authSession, store } from 'solid-logic'
import { NamedNode } from 'rdflib'

@SharonStrats SharonStrats Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from ai

  1. Keep editContext.profileData synchronized with the displayed data

When a profile-pane-saved event includes fresh profileData, the handler
updates heading.profileData but leaves heading.editContext.profileData
unchanged.

This can make the visible heading show newly saved contact or job information
while the edit dialog still opens with stale values. Please update the complete
edit context whenever the displayed profile data is replaced.

  1. Avoid refreshing for every mutation in the entire MainContent subtree

The MutationObserver watches the complete MainContent subtree and calls
refreshProfileHeading() for every child-list mutation. Pane rendering and
component updates can generate many mutations that are unrelated to navigation
or the displayed profile.

Please narrow the observer to relevant navigation or pane changes, or
coalesce refreshes so a burst of DOM mutations results in one refresh.

  1. Make the profile-heading mount positioning resilient to initialization order

ensureMountPoint() positions the mount relative to the navbar when the navbar
already exists, but initialization can call it before the navbar is created.
In that case the mount is appended to MainContent and may remain in the
wrong position after the navbar is later inserted.

Please make ensureMountPoint() reposition an existing mount whenever the
navbar or outline insertion point changes, rather than relying on the caller
to invoke it again in the correct order.

  1. Avoid using broad element casts for the profile-heading contract

The code repeatedly casts the custom element to an inline structural type to
assign profileData, editContext, friendSubject, and compact.

Please expose a typed ProfileHeading element type or use the exported
component class when querying the mount. This would keep the component
contract in one place and make future property changes safer.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only addressed the first here, rest in the refactoring to navigation controller.

Comment thread vite.config.mts Outdated
build.rolldownOptions.output = build.rolldownOptions.output.filter(
(o: { format?: string }) => o.format === 'es',
)
build.emptyOutDir = false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this I think the new toolkit this shouldn't be needed.

@SharonStrats SharonStrats left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh my :) LOL
I have left so many comments about things we should do a bit differently (not from your specific work, work that you had to copy over), most I think we should create tickets to do later after we release.
I'm not sure if it's helpful or not but I asked ai to look at a few files, it was just so much for me to review my computer isn't even handling me typing in these comments the PR is so big LOL.
Here is one more comment from review ai

Broad `MutationObserver` refresh

The profile heading observes the entire `MainContent` subtree and calls
`refreshProfileHeading()` for every mutation. The pane-key checks prevent an
obvious correctness loop, but navigation and pane rendering can generate many
mutations and redundant refresh attempts.

This is lower priority, but the observer could eventually filter relevant
mutations or debounce refreshes.

I will see if I can fix the build for you.
I have tested locally and it works really well and looks very nice the way it flows.
great job!!

timea-solid and others added 9 commits October 13, 2026 13:52
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Add file size validation for selected images.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…istry@5.0.2-0) (latest: rdflib@2.4.1 solid-ui@5.0.1 activitystreams-pane@2.0.0 chat-pane@4.0.0 contacts-pane@4.0.0 folder-pane@4.0.0 issue-pane@4.0.0 meeting-pane@4.0.0 profile-pane@4.0.0 source-pane@4.0.0)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Remove the call to toLowerCase() in sanitizeEmailValue function.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Set dialog tabIndex to -1 and focus on it in mobile layout.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…istry@5.0.2-0) (latest: rdflib@2.4.1 solid-ui@5.0.1 activitystreams-pane@2.0.0 chat-pane@4.0.0 contacts-pane@4.0.0 folder-pane@4.0.0 issue-pane@4.0.0 meeting-pane@4.0.0 profile-pane@4.0.0 source-pane@4.0.0)
@timea-solid

Copy link
Copy Markdown
Member Author

Thank you @SharonStrats! Pls have a final review and let me know how it is so I can improve or merge.

timea-solid and others added 5 commits October 13, 2026 21:30
…istry@5.0.2-0) (latest: rdflib@2.4.1 solid-ui@5.0.1 activitystreams-pane@2.0.0 chat-pane@4.0.0 contacts-pane@4.0.0 folder-pane@4.0.0 issue-pane@4.0.0 meeting-pane@4.0.0 profile-pane@4.0.0 source-pane@4.0.0)
…istry@5.0.2-0 profile-pane@4.1.0-0) (latest: rdflib@2.4.1 solid-ui@5.0.1 activitystreams-pane@2.0.0 chat-pane@4.0.0 contacts-pane@4.0.0 folder-pane@4.0.0 issue-pane@4.0.0 meeting-pane@4.0.0 source-pane@4.0.0)
@timea-solid
timea-solid added this pull request to stack #772 October 14, 2026 08:21
…istry@5.0.2-0 profile-pane@4.1.0-0) (latest: rdflib@2.4.1 solid-ui@5.0.1 activitystreams-pane@2.0.0 chat-pane@4.0.0 contacts-pane@4.0.0 folder-pane@4.0.0 issue-pane@4.0.0 meeting-pane@4.0.0 source-pane@4.0.0)
@timea-solid
timea-solid merged commit cfa095e into staging Oct 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Create the Heading component on top of all panes

3 participants