Repository navigation
New Heading component - #766
Conversation
…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>
…6.0.0 solid-ui@5.0.1 pane-registry@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)
There was a problem hiding this comment.
🟡 Changes recommended
Data integrity, upload safety, phone parsing, and accessibility issues remain unresolved.
8 open findings
Make profile mutations atomic · New Allow long profile names to wrap on mobile · New Validate required full name before saving · New Enforce the advertised 2 MB photo size limit · New Match phone numbers against normalized country prefixes · New Preserve email local-part casing · New Keep keyboard focus inside the mobile dialog · New Exclude future roles from the no-end-date fallback · New
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.
| accessor profileData: ProfileHeadingData | undefined = undefined | ||
|
|
||
| @property({ attribute: false }) | ||
| accessor editContext: ProfileHeadingEditContext | undefined = undefined |
There was a problem hiding this comment.
this could become stale because it is not updated on profile-pane-saved
There was a problem hiding this comment.
I am not sure what you mean here..
There was a problem hiding this comment.
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.
| 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' |
There was a problem hiding this comment.
Here is some feedback I received from ai
- 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.
- 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.
- 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.
- 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.
- 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.
| } | ||
| } | ||
|
|
||
| export function presentProfile (subject: NamedNode, profileStore: LiveStore = store): ProfileDetails { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Yes, this needs to be thought trough and discussed a bit. I make a ticket. SolidOS/solidos#369
| } | ||
|
|
||
| // The header outlives logins, so the viewer is resolved when an item is chosen. | ||
| async function currentViewer () { |
There was a problem hiding this comment.
I wonder if this shoudl also live with the viewer mode stuff
There was a problem hiding this comment.
I consolidated this.
| @@ -32,43 +32,51 @@ export async function createHeader (outliner: OutlineManager) { | |||
| } | |||
There was a problem hiding this comment.
Feedback from ai
- 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.
- 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.
- 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.
There was a problem hiding this comment.
All these will be addressed in the navigation refactoring PR. (will follow)
| @@ -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' | |||
There was a problem hiding this comment.
from ai
- 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.
- 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.
There was a problem hiding this comment.
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' | |||
There was a problem hiding this comment.
from ai
- Keep
editContext.profileDatasynchronized 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.
- Avoid refreshing for every mutation in the entire
MainContentsubtree
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.
- 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.
- 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.
There was a problem hiding this comment.
I only addressed the first here, rest in the refactoring to navigation controller.
| build.rolldownOptions.output = build.rolldownOptions.output.filter( | ||
| (o: { format?: string }) => o.format === 'es', | ||
| ) | ||
| build.emptyOutDir = false |
There was a problem hiding this comment.
I'm not sure about this I think the new toolkit this shouldn't be needed.
There was a problem hiding this comment.
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!!
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)
|
Thank you @SharonStrats! Pls have a final review and let me know how it is so I can improve or merge. |
…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)
…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)


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.