Skip to content

Commit

Permalink
Add UI for AI Chat (#489)
Browse files Browse the repository at this point in the history
* Add empty chat view

* Add UI for chat

* Store chat history in localStorage, add button

* Change placeholder color

* Implement UI for (dummy) AI response

* Get chat response from API

* Change icon to family tree

* Add  loading animation, render markdown links

* Change type to role, submit history

* Update plugin

* Error handling in chat

* Fix lint error

* Add clear button to chat

* Add string

* Only show chat if authorised to use

* lint issues

* Add UI to manage chat permissions

* Add button group component

* Add UI for semantic search

* UI for chat quota

* Add UI to manage semantic search index
  • Loading branch information
DavidMStraub authored Sep 25, 2024
1 parent a1575db commit f1d53d8
Show file tree
Hide file tree
Showing 22 changed files with 1,241 additions and 144 deletions.
7 changes: 6 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,10 @@
"This action cannot be undone.": "This action cannot be undone.",
"Yes": "Yes",
"Toggle time filter for places": "Toggle time filter for places",
"The search index needs to be rebuilt.": "The search index needs to be rebuilt."
"The search index needs to be rebuilt.": "The search index needs to be rebuilt.",
"Ask something about your ancestors": "Ask something about your ancestors",
"Chat messages": "Chat messages",
"Manage semantic search index": "Manage semantic search index",
"Update semantic search index": "Update semantic search index",
"Updating the semantic search index requires substantial time and computational resources. Run this operation only when necessary.": "Updating the semantic search index requires substantial time and computational resources. Run this operation only when necessary."
}
329 changes: 252 additions & 77 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"eslint-config-prettier": "^7.2.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-lit-a11y": "^4.1.4",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"fetch-mock": "^9.11.0",
Expand Down Expand Up @@ -127,4 +128,4 @@
"pwa-helpers": "^0.9.1",
"tippy.js": "^6.3.7"
}
}
}
33 changes: 18 additions & 15 deletions src/GrampsJs.js
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ export class GrampsJs extends LitElement {
<grampsjs-main-menu
.strings="${this._strings}"
?canViewPrivate="${this.canViewPrivate}"
?canUseChat="${this.canUseChat}"
></grampsjs-main-menu>
</div>
<div slot="appContent">
Expand All @@ -547,6 +548,7 @@ export class GrampsJs extends LitElement {
.canEdit="${this.canEdit}"
.canViewPrivate="${this.canViewPrivate}"
.canManageUsers="${this.canManageUsers}"
.canUseChat="${this.canUseChat}"
>
</grampsjs-pages>
</main>
Expand Down Expand Up @@ -604,7 +606,8 @@ export class GrampsJs extends LitElement {
this.addEventListener('drawer:toggle', this._toggleDrawer)
window.addEventListener('keydown', event => this._handleKey(event))
document.addEventListener('visibilitychange', this._handleVisibilityChange)
window.addEventListener('online', this._handleOnline)
window.addEventListener('online', () => this._handleOnline())
window.addEventListener('token:refresh', () => this._handleRefresh())

const browserLang = getBrowserLanguage()
if (browserLang && !this.settings.lang) {
Expand Down Expand Up @@ -834,13 +837,19 @@ export class GrampsJs extends LitElement {
_handleVisibilityChange() {
if (document.visibilityState === 'visible') {
// refresh auth token when app becomes visible again
apiRefreshAuthToken()
this._handleRefresh()
}
}

// eslint-disable-next-line class-methods-use-this
_handleOnline() {
apiRefreshAuthToken()
this._handleRefresh()
}

// eslint-disable-next-line class-methods-use-this
async _handleRefresh() {
await apiRefreshAuthToken()
this.setPermissions()
}

update(changed) {
Expand Down Expand Up @@ -994,18 +1003,12 @@ export class GrampsJs extends LitElement {
setPermissions() {
const permissions = getPermissions()
// If permissions is null, authorization is disabled and anything goes
if (permissions === null) {
this.canAdd = true
this.canEdit = true
this.canViewPrivate = true
// managing users not meaningful in this case
this.canManageUsers = false
} else {
this.canAdd = permissions.includes('AddObject')
this.canEdit = permissions.includes('EditObject')
this.canViewPrivate = permissions.includes('ViewPrivate')
this.canManageUsers = permissions.includes('EditOtherUser')
}
this.canAdd = permissions.includes('AddObject')
this.canEdit = permissions.includes('EditObject')
this.canViewPrivate = permissions.includes('ViewPrivate')
this.canManageUsers = permissions.includes('EditOtherUser')
this.canUseChat =
permissions.includes('UseChat') && this._dbInfo?.server?.chat
}

_(s) {
Expand Down
12 changes: 12 additions & 0 deletions src/SharedStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@ export const sharedStyles = css`
border-left-color: rgba(251, 192, 45, 0.7);
}
.success {
color: #41ad49;
}
.error {
color: #bf360c;
}
.warn {
color: #f9a825;
}
@keyframes shine {
to {
background-position-x: -200%;
Expand Down
27 changes: 27 additions & 0 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,33 @@ export function setRecentObjects(data) {
localStorage.setItem('recentObjects', stringData)
}

export function getChatHistory() {
try {
const string = localStorage.getItem('chatMessages')
const data = JSON.parse(string)
const tree = getTreeId()
if (tree) {
return data[tree]
}
return []
} catch (e) {
return []
}
}

export function setChatHistory(data) {
const tree = getTreeId()
if (!tree) {
return
}
const stringDataAll = localStorage.getItem('chatMessages')
const objectDataAll = JSON.parse(stringDataAll)
const objectDataNew = {[tree]: data}
const objectData = {...objectDataAll, ...objectDataNew}
const stringData = JSON.stringify(objectData)
localStorage.setItem('chatMessages', stringData)
}

export async function apiResetPassword(username) {
try {
const resp = await fetch(
Expand Down
41 changes: 41 additions & 0 deletions src/components/GrampsjsButtonGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {LitElement, html, css} from 'lit'

import '@material/mwc-icon-button'

import {sharedStyles} from '../SharedStyles.js'
import {GrampsjsTranslateMixin} from '../mixins/GrampsjsTranslateMixin.js'
import './GrampsjsTooltip.js'

export class GrampsjsButtonGroup extends GrampsjsTranslateMixin(LitElement) {
static get styles() {
return [
sharedStyles,
css`
#button-container {
--mdc-typography-button-font-size: 12px;
margin: 12px 0;
}
#button-container div {
border: 1px solid var(--mdc-theme-primary);
opacity: 0.9;
border-radius: 8px;
display: inline-block;
padding: 4px;
}
`,
]
}

render() {
return html`
<div id="button-container">
<div>
<slot></slot>
</div>
</div>
`
}
}

window.customElements.define('grampsjs-button-group', GrampsjsButtonGroup)
Loading

0 comments on commit f1d53d8

Please sign in to comment.