index.html (20170B)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <link rel="preconnect" href="https://fonts.googleapis.com"> 7 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 8 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=EB+Garamond:ital,wght@0,400;0,600;1,400&display=swap" rel="stylesheet"> 9 <link rel="stylesheet" href="/static/style.css"> 10 <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>π³</text></svg>"> 11 <title>Family Tree</title> 12 </head> 13 <body> 14 15 <!-- Topbar --> 16 <nav class="topbar"> 17 <div class="topbar-inner"> 18 <div class="topbar-left"> 19 <span class="topbar-name">Family Tree</span> 20 <button class="btn-sidebar-toggle" id="sidebarToggle" data-i18n-title="toggle-sidebar" title="Toggle sidebar"> 21 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 22 <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5M3.75 17.25h16.5"/> 23 </svg> 24 </button> 25 </div> 26 <div class="topbar-right"> 27 <span class="topbar-username" id="topbarUsername"></span> 28 <a href="https://auth.piribauer.ch/if/user/" class="nav-link" target="_blank" rel="noopener" data-i18n="account">Account</a> 29 <a href="/outpost.goauthentik.io/sign_out" class="nav-link signout" data-i18n="sign-out">Sign out</a> 30 <div class="lang-switcher"> 31 <button class="lang-opt lang-active" data-lang="en" onclick="setLang('en')">EN</button> 32 <button class="lang-opt" data-lang="de" onclick="setLang('de')">DE</button> 33 </div> 34 <a href="https://hub.piribauer.ch" class="topbar-home" aria-label="Hub"> 35 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"> 36 <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"/> 37 </svg> 38 </a> 39 </div> 40 </div> 41 </nav> 42 43 <!-- App body --> 44 <div class="app-body"> 45 46 <!-- Sidebar --> 47 <aside class="sidebar" id="sidebar"> 48 <div class="sidebar-header" data-i18n="sidebar-trees">Trees</div> 49 <div class="tree-list" id="treeList"></div> 50 <div class="sidebar-actions"> 51 <button class="btn-new-tree" id="btnNewTree"> 52 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 53 <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/> 54 </svg> 55 <span data-i18n="new-tree">New tree</span> 56 </button> 57 </div> 58 </aside> 59 60 <!-- Canvas area --> 61 <main class="canvas-area" id="canvasArea"> 62 63 <!-- Empty state --> 64 <div class="empty-state" id="emptyState"> 65 <div class="empty-icon"> 66 <svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.5"> 67 <circle cx="24" cy="10" r="4"/> 68 <line x1="24" y1="14" x2="24" y2="22"/> 69 <line x1="24" y1="22" x2="12" y2="30"/> 70 <line x1="24" y1="22" x2="36" y2="30"/> 71 <circle cx="12" cy="34" r="4"/> 72 <circle cx="36" cy="34" r="4"/> 73 </svg> 74 </div> 75 <p class="empty-title" data-i18n="empty-title">No tree selected</p> 76 <p class="empty-sub" data-i18n="empty-sub">Create a tree or select one from the sidebar</p> 77 <button class="btn btn-primary" id="emptyNewTree" data-i18n="create-tree-btn">Create a tree</button> 78 </div> 79 80 <!-- Canvas toolbar --> 81 <div class="canvas-toolbar" id="canvasToolbar" style="display:none"> 82 <button class="toolbar-btn" id="btnZoomIn" data-i18n-title="zoom-in" title="Zoom in"> 83 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 84 <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35M11 8v6M8 11h6"/> 85 </svg> 86 </button> 87 <button class="toolbar-btn" id="btnZoomOut" data-i18n-title="zoom-out" title="Zoom out"> 88 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 89 <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35M8 11h6"/> 90 </svg> 91 </button> 92 <button class="toolbar-btn" id="btnFit" data-i18n-title="fit-screen" title="Fit to screen"> 93 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 94 <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9M20.25 20.25h-4.5m4.5 0v-4.5m0 4.5L15 15"/> 95 </svg> 96 </button> 97 <div class="toolbar-sep"></div> 98 <button class="toolbar-btn" id="btnDownloadPDF" data-i18n-title="download-pdf" title="Download PDF"> 99 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 100 <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12M12 16.5V3"/> 101 </svg> 102 <span data-i18n="download-pdf">PDF</span> 103 </button> 104 <div class="toolbar-sep"></div> 105 <button class="toolbar-btn toolbar-btn-primary" id="btnAddPerson" data-i18n-title="add-person" title="Add person"> 106 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 107 <path stroke-linecap="round" stroke-linejoin="round" d="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z"/> 108 </svg> 109 <span data-i18n="add-person">Add person</span> 110 </button> 111 </div> 112 113 <!-- SVG canvas --> 114 <svg id="treeCanvas" style="display:none"></svg> 115 116 </main> 117 118 <!-- Detail panel --> 119 <aside class="detail-panel" id="detailPanel"> 120 <div class="detail-header"> 121 <span class="detail-name" id="detailName"></span> 122 <div class="detail-header-actions"> 123 <button class="btn-icon" id="btnEditPerson" data-i18n-title="btn-edit" title="Edit"> 124 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 125 <path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125"/> 126 </svg> 127 </button> 128 <button class="btn-icon danger" id="btnDeletePerson" data-i18n-title="btn-delete" title="Delete"> 129 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 130 <path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"/> 131 </svg> 132 </button> 133 <button class="btn-icon" id="btnCloseDetail" data-i18n-title="btn-close" title="Close"> 134 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 135 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 136 </svg> 137 </button> 138 </div> 139 </div> 140 <div class="detail-body" id="detailBody"></div> 141 </aside> 142 143 </div> 144 145 <!-- Context menu --> 146 <div class="ctx-menu" id="ctxMenu"> 147 <div class="ctx-item" id="ctxRename"> 148 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:14px;height:14px"> 149 <path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487z"/> 150 </svg> 151 <span data-i18n="ctx-rename">Rename</span> 152 </div> 153 <div class="ctx-item" id="ctxMembers"> 154 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:14px;height:14px"> 155 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z"/> 156 </svg> 157 <span data-i18n="ctx-members">Manage members</span> 158 </div> 159 <div class="ctx-item" id="ctxLeave"> 160 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:14px;height:14px"> 161 <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9"/> 162 </svg> 163 <span data-i18n="ctx-leave">Leave tree</span> 164 </div> 165 <div class="ctx-item danger" id="ctxDelete"> 166 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:14px;height:14px"> 167 <path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"/> 168 </svg> 169 <span data-i18n="ctx-delete">Delete tree</span> 170 </div> 171 </div> 172 173 <!-- ββ Modals ββ --> 174 175 <!-- New/rename tree --> 176 <div class="modal-overlay" id="modalTree"> 177 <div class="modal"> 178 <h2 id="modalTreeTitle" data-i18n="new-tree-title">New tree</h2> 179 <div class="form-group"> 180 <label class="form-label" data-i18n="form-name">Name</label> 181 <input class="form-input" id="treeNameInput" type="text" data-i18n-placeholder="tree-name-ph" placeholder="e.g. Smith family"> 182 </div> 183 <div class="modal-actions"> 184 <button class="btn" id="btnTreeCancel" data-i18n="cancel">Cancel</button> 185 <button class="btn btn-primary" id="btnTreeSave" data-i18n="create">Create</button> 186 </div> 187 </div> 188 </div> 189 190 <!-- Add/edit person --> 191 <div class="modal-overlay" id="modalPerson"> 192 <div class="modal modal-wide"> 193 <h2 id="modalPersonTitle" data-i18n="add-person-title">Add person</h2> 194 <div class="form-row"> 195 <div class="form-group"> 196 <label class="form-label" data-i18n="form-first-name">First name</label> 197 <input class="form-input" id="personFirstName" type="text" data-i18n-placeholder="form-first-name" placeholder="First name"> 198 </div> 199 <div class="form-group"> 200 <label class="form-label" data-i18n="form-last-name">Last name</label> 201 <input class="form-input" id="personLastName" type="text" data-i18n-placeholder="form-last-name" placeholder="Last name"> 202 </div> 203 </div> 204 <div class="form-row"> 205 <div class="form-group"> 206 <label class="form-label" data-i18n="form-birth-date">Birth date</label> 207 <input class="form-input" id="personBirthDate" type="text" placeholder="DD.MM.YYYY"> 208 </div> 209 <div class="form-group"> 210 <label class="form-label" data-i18n="form-death-date">Death date</label> 211 <input class="form-input" id="personDeathDate" type="text" placeholder="DD.MM.YYYY"> 212 </div> 213 </div> 214 <div class="form-row"> 215 <div class="form-group"> 216 <label class="form-label" data-i18n="form-birth-place">Birth place</label> 217 <input class="form-input" id="personBirthPlace" type="text" placeholder="City, Country"> 218 </div> 219 <div class="form-group"> 220 <label class="form-label" data-i18n="form-burial-place">Burial place</label> 221 <input class="form-input" id="personBurialPlace" type="text" placeholder="Cemetery, City"> 222 </div> 223 </div> 224 <div class="form-group"> 225 <label class="form-label" data-i18n="form-biography">Biography</label> 226 <textarea class="form-input" id="personBio" rows="3" data-i18n-placeholder="bio-ph" placeholder="A short noteβ¦"></textarea> 227 </div> 228 <div class="modal-actions"> 229 <button class="btn" id="btnPersonCancel" data-i18n="cancel">Cancel</button> 230 <button class="btn btn-primary" id="btnPersonSave" data-i18n="save">Save</button> 231 </div> 232 </div> 233 </div> 234 235 <!-- Connect new person --> 236 <div class="modal-overlay" id="modalConnect"> 237 <div class="modal"> 238 <h2>Connect <span id="connectPersonName" style="color:var(--lightblue)"></span></h2> 239 <p style="font-size:0.875rem;color:var(--snow1);opacity:0.6;margin-bottom:1.25rem" data-i18n="connect-how"> 240 How would you like to add this person to the tree? 241 </p> 242 <div class="connect-options"> 243 <button class="btn connect-btn" id="connectAsPartner"> 244 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 003.741-.479 3 3 0 00-4.682-2.72m.94 3.198l.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0112 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 016 18.719m12 0a5.971 5.971 0 00-.941-3.197m0 0A5.995 5.995 0 0012 12.75a5.995 5.995 0 00-5.058 2.772m0 0a3 3 0 00-4.681 2.72 8.986 8.986 0 003.74.477m.94-3.197a5.971 5.971 0 00-.94 3.197M15 6.75a3 3 0 11-6 0 3 3 0 016 0zm6 3a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0zm-13.5 0a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z"/></svg> 245 <span data-i18n="connect-partner">Partner of someone</span> 246 </button> 247 <button class="btn connect-btn" id="connectAsChild"> 248 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 13.5L12 21m0 0l-7.5-7.5M12 21V3"/></svg> 249 <span data-i18n="connect-child">Child of someone</span> 250 </button> 251 <button class="btn connect-btn" id="connectAsParent"> 252 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 10.5L12 3m0 0l7.5 7.5M12 3v18"/></svg> 253 <span data-i18n="connect-parent">Parent of someone</span> 254 </button> 255 <button class="btn connect-btn" id="connectAsSibling"> 256 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5"/></svg> 257 <span data-i18n="connect-sibling">Sibling of someone</span> 258 </button> 259 </div> 260 <div class="modal-actions"> 261 <button class="btn" id="btnConnectSkip" data-i18n="skip-for-now">Skip for now</button> 262 </div> 263 </div> 264 </div> 265 266 <!-- Add/edit partnership --> 267 <div class="modal-overlay" id="modalPartnership"> 268 <div class="modal"> 269 <h2 id="modalPartnershipTitle" data-i18n="add-partnership-title">Add partnership</h2> 270 <div class="form-group"> 271 <label class="form-label" data-i18n="form-person1">Person 1</label> 272 <select class="form-input" id="partnerPerson1"></select> 273 </div> 274 <div class="form-group"> 275 <label class="form-label" data-i18n="form-person2">Person 2</label> 276 <select class="form-input" id="partnerPerson2"></select> 277 </div> 278 <div class="form-group"> 279 <label class="form-label" data-i18n="form-type">Type</label> 280 <select class="form-input" id="partnerType"> 281 <option value="married" data-i18n="type-married">Married</option> 282 <option value="partnered" selected data-i18n="type-partnered">Partnered</option> 283 <option value="separated" data-i18n="type-separated">Separated</option> 284 </select> 285 </div> 286 <div class="form-row"> 287 <div class="form-group"> 288 <label class="form-label" data-i18n="form-start-date">Start date</label> 289 <input class="form-input" id="partnerStart" type="text" placeholder="YYYY-MM-DD"> 290 </div> 291 <div class="form-group"> 292 <label class="form-label" data-i18n="form-end-date">End date</label> 293 <input class="form-input" id="partnerEnd" type="text" placeholder="YYYY-MM-DD"> 294 </div> 295 </div> 296 <div class="modal-actions"> 297 <button class="btn" id="btnPartnerCancel" data-i18n="cancel">Cancel</button> 298 <button class="btn btn-primary" id="btnPartnerSave" data-i18n="save">Save</button> 299 </div> 300 </div> 301 </div> 302 303 <!-- Add parent link --> 304 <div class="modal-overlay" id="modalParentLink"> 305 <div class="modal"> 306 <h2 data-i18n="add-parent-link-title">Add parent link</h2> 307 <div class="form-group"> 308 <label class="form-label" data-i18n="form-child">Child</label> 309 <select class="form-input" id="linkChild"></select> 310 </div> 311 <div class="form-group"> 312 <label class="form-label" data-i18n="form-link-via">Link via</label> 313 <select class="form-input" id="linkViaType" onchange="app.onLinkViaTypeChange()"> 314 <option value="partnership" data-i18n="link-partnership">Partnership (two parents)</option> 315 <option value="single" data-i18n="link-single-parent">Single parent</option> 316 </select> 317 </div> 318 <div class="form-group" id="linkPartnershipGroup"> 319 <label class="form-label" data-i18n="form-partnership">Partnership</label> 320 <select class="form-input" id="linkPartnership"></select> 321 </div> 322 <div class="form-group" id="linkParentGroup" style="display:none"> 323 <label class="form-label" data-i18n="form-parent">Parent</label> 324 <select class="form-input" id="linkParent"></select> 325 </div> 326 <div class="modal-actions"> 327 <button class="btn" id="btnLinkCancel" data-i18n="cancel">Cancel</button> 328 <button class="btn btn-primary" id="btnLinkSave" data-i18n="save">Save</button> 329 </div> 330 </div> 331 </div> 332 333 <!-- Manage members --> 334 <div class="modal-overlay" id="modalMembers"> 335 <div class="modal modal-wide"> 336 <h2 data-i18n="members-title">Members</h2> 337 <div id="membersList"></div> 338 <div class="members-invite-section"> 339 <p class="form-label" style="margin-bottom:0.6rem;margin-top:1.25rem" data-i18n="add-by-username">Add by username</p> 340 <div class="invite-row"> 341 <input class="form-input" id="inviteUsername" type="text" data-i18n-placeholder="username-ph" placeholder="username"> 342 <select class="form-input" id="inviteRole" style="width:110px;flex-shrink:0"> 343 <option value="editor" data-i18n="role-editor">Editor</option> 344 <option value="viewer" selected data-i18n="role-viewer">Viewer</option> 345 </select> 346 <button class="btn btn-primary" id="btnInviteSend" data-i18n="invite-btn">Add</button> 347 </div> 348 <div id="pendingList"></div> 349 </div> 350 <div class="modal-actions"> 351 <button class="btn btn-primary" id="btnMembersClose" data-i18n="done">Done</button> 352 </div> 353 </div> 354 </div> 355 356 <script src="/static/d3.v7.min.js"></script> 357 <script src="/static/i18n.js"></script> 358 <script src="/static/app.js"></script> 359 </body> 360 </html>