aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/newlightbox/Header/LightboxHeader.scss21
-rw-r--r--src/client/views/newlightbox/Header/LightboxHeader.tsx25
-rw-r--r--src/client/views/newlightbox/RecommendationList/RecommendationList.scss14
-rw-r--r--src/client/views/newlightbox/RecommendationList/RecommendationList.tsx42
-rw-r--r--src/client/views/newlightbox/utils.ts31
-rw-r--r--src/client/views/search/SearchBox.scss65
-rw-r--r--src/client/views/search/SearchBox.tsx57
7 files changed, 160 insertions, 95 deletions
diff --git a/src/client/views/newlightbox/Header/LightboxHeader.scss b/src/client/views/newlightbox/Header/LightboxHeader.scss
index 2872a383b..a9e60ea98 100644
--- a/src/client/views/newlightbox/Header/LightboxHeader.scss
+++ b/src/client/views/newlightbox/Header/LightboxHeader.scss
@@ -59,26 +59,7 @@
background: $blue-l1;
}
}
-
- .lb-button2 {
- padding: 3px;
- cursor: pointer;
- display: flex;
- flex-direction: row;
- justify-content: space-evenly;
- align-items: center;
- transition: 0.2s ease;
- gap: 5px;
- font-size: 15px;
- height: fit-content;
- border-radius: 3px;
-
- &:hover {
- background: $gray-l2;
- transform: scale(1.01);
- }
- }
-
+
&.dark {
background: $black;
}
diff --git a/src/client/views/newlightbox/Header/LightboxHeader.tsx b/src/client/views/newlightbox/Header/LightboxHeader.tsx
index a542d2943..a272ce294 100644
--- a/src/client/views/newlightbox/Header/LightboxHeader.tsx
+++ b/src/client/views/newlightbox/Header/LightboxHeader.tsx
@@ -5,21 +5,23 @@ import { NewLightboxView } from '../NewLightboxView';
import { StrCast } from '../../../../fields/Types';
import { EditableText } from '../components/EditableText';
import { getType } from '../utils';
-import { Button, Size, Type } from 'browndash-components';
+import { Button, IconButton, Size, Type } from 'browndash-components';
import { MdExplore, MdTravelExplore } from 'react-icons/md'
import { BsBookmark, BsBookmarkFill } from 'react-icons/bs'
import { Doc } from '../../../../fields/Doc';
+import { LightboxView } from '../../LightboxView';
+import { Colors } from '../../global/globalEnums';
export const NewLightboxHeader = (props: INewLightboxHeader) => {
const {height = 100, width} = props;
- const [doc, setDoc] = React.useState<Doc | undefined>(NewLightboxView.LightboxDoc)
+ const [doc, setDoc] = React.useState<Doc | undefined>(LightboxView.LightboxDoc)
const [editing, setEditing] = React.useState<boolean>(false)
const [title, setTitle] = React.useState<JSX.Element | null>(
(null)
)
React.useEffect(() => {
- let lbDoc = NewLightboxView.LightboxDoc
+ let lbDoc = LightboxView.LightboxDoc
setDoc(lbDoc)
if (lbDoc) {
setTitle(
@@ -32,7 +34,7 @@ export const NewLightboxHeader = (props: INewLightboxHeader) => {
setEditing={setEditing}
/>)
}
- }, [NewLightboxView.LightboxDoc])
+ }, [LightboxView.LightboxDoc])
const [saved, setSaved] = React.useState<boolean>(false)
@@ -47,21 +49,14 @@ export const NewLightboxHeader = (props: INewLightboxHeader) => {
<div className={`type`}>{getType(StrCast(doc.type))}</div>
</div>
<div style={{gridColumn: 2, gridRow: 1, height: '100%', display: 'flex', justifyContent: 'flex-end', alignItems: 'center'}}>
- <div className={`lb-button2`} onClick={() => setSaved(!saved)}>
- {saved ? <BsBookmarkFill/> : <BsBookmark/>}
- </div>
- <div className={`lb-button2`} onClick={() => setSaved(!saved)}>
- {saved ? <BsBookmarkFill/> : <BsBookmark/>}
- </div>
+ <IconButton size={Size.XSMALL} onClick={() => setSaved(!saved)} color={Colors.DARK_GRAY} icon={saved ? <BsBookmarkFill/> : <BsBookmark/>}/>
+ <IconButton size={Size.XSMALL} onClick={() => setSaved(!saved)} color={Colors.DARK_GRAY} icon={saved ? <BsBookmarkFill/> : <BsBookmark/>}/>
</div>
<div style={{gridColumn: 2, gridRow: 2, height: '100%', display: 'flex', justifyContent: 'flex-end', alignItems: 'center'}}>
- <div className={`lb-button ${NewLightboxView.ExploreMode}`} onClick={() => {
+ <Button onClick={() => {
console.log(NewLightboxView.ExploreMode)
NewLightboxView.SetExploreMode(!NewLightboxView.ExploreMode)
- }}>
- <MdTravelExplore/>
- t-SNE 2D Embeddings
- </div>
+ }} size={Size.XSMALL} color={Colors.DARK_GRAY} type={Type.SEC} text={"t-SNE 2D Embeddings"} icon={<MdTravelExplore/>}/>
</div>
</div>
} \ No newline at end of file
diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.scss b/src/client/views/newlightbox/RecommendationList/RecommendationList.scss
index e59834353..40dd47e47 100644
--- a/src/client/views/newlightbox/RecommendationList/RecommendationList.scss
+++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.scss
@@ -101,20 +101,6 @@
min-width: 70px;
height: 21.5px;
}
-
- .remove {
- cursor: pointer;
- display: flex;
- justify-content: center;
- align-items: center;
- color: $text-color-lm;
- padding: 1.5px;
- border-radius: 2px;
-
- &:hover {
- background: $gray-l1;
- }
- }
}
}
diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
index 674a501a7..9f3c32e4e 100644
--- a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
+++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
@@ -12,6 +12,8 @@ import { IBounds } from '../ExploreView/utils';
import { List } from '../../../../fields/List';
import { Id } from '../../../../fields/FieldSymbols';
import { LightboxView } from '../../LightboxView';
+import { IconButton, Size, Type } from 'browndash-components';
+import { Colors } from '../../global/globalEnums';
export const RecommendationList = (props: IRecommendationList) => {
const {loading, keywords} = props
@@ -31,8 +33,9 @@ export const RecommendationList = (props: IRecommendationList) => {
React.useEffect(() => {
const getKeywords = async () => {
let text = StrCast(LightboxView.LightboxDoc?.text)
- console.log('fetching keywords w/: ', text)
+ console.log('[1] fetching keywords')
const response = await fetchKeywords(text, 5, true)
+ console.log('[2] response:', response)
const kw = response.keywords;
console.log(kw);
NewLightboxView.SetKeywords(kw);
@@ -59,30 +62,30 @@ export const RecommendationList = (props: IRecommendationList) => {
React.useEffect(() => {
const getRecommendations = async () => {
console.log('fetching recommendations')
- let query = undefined
+ let query = 'undefined'
if (keywordsLoc) query = keywordsLoc.join(',')
let src = StrCast(NewLightboxView.LightboxDoc?.text)
let dashDocs:IDocRequest[] = [];
// get linked docs
let linkedDocs = DocListCast(NewLightboxView.LightboxDoc?.links)
console.log("linked docs", linkedDocs)
- // get context docs
- let contextDocs: Doc[] = DocListCast(DocCast(NewLightboxView.LightboxDoc?.context).data)
- let docId = NewLightboxView.LightboxDoc && NewLightboxView.LightboxDoc[Id]
- console.log("context docs", contextDocs)
- contextDocs.forEach((doc: Doc) => {
- if (docId !== doc[Id]){
- dashDocs.push({
- title: StrCast(doc.title),
- text: StrCast(doc.text),
- id: doc[Id],
- type: StrCast(doc.type)
- })
- }
- })
+ // get context docs (docs that are also in the collection)
+ // let contextDocs: Doc[] = DocListCast(DocCast(LightboxView.LightboxDoc?.context).data)
+ // let docId = LightboxView.LightboxDoc && LightboxView.LightboxDoc[Id]
+ // console.log("context docs", contextDocs)
+ // contextDocs.forEach((doc: Doc) => {
+ // if (docId !== doc[Id]){
+ // dashDocs.push({
+ // title: StrCast(doc.title),
+ // text: StrCast(doc.text),
+ // id: doc[Id],
+ // type: StrCast(doc.type)
+ // })
+ // }
+ // })
console.log("dash docs", dashDocs)
if (query !== undefined) {
- const response = await fetchRecommendations(src, query, dashDocs, true)
+ const response = await fetchRecommendations(src, query, [], true)
const num_recs = response.num_recommendations
const recs = response.recommendations
const keywords = response.keywords
@@ -131,6 +134,7 @@ export const RecommendationList = (props: IRecommendationList) => {
return a.distance - b.distance
} else return 0
})
+ console.log("[rec]: ", recommendations)
NewLightboxView.SetRecs(recommendations)
setRecs(recommendations)
}
@@ -160,11 +164,11 @@ export const RecommendationList = (props: IRecommendationList) => {
{keywordsLoc && keywordsLoc.map((word, ind) => {
return <div className={`keyword`}>
{word}
- <div className={`remove`} onClick={() => {
+ <IconButton type={Type.PRIM} size={Size.XSMALL} color={Colors.DARK_GRAY} icon={<GrClose/>} onClick={() => {
let kw = keywordsLoc
kw.splice(ind)
NewLightboxView.SetKeywords(kw)
- }}>{<GrClose/>}</div>
+ }}/>
</div>
})}
</div>
diff --git a/src/client/views/newlightbox/utils.ts b/src/client/views/newlightbox/utils.ts
index 29b83c4e0..6016abca4 100644
--- a/src/client/views/newlightbox/utils.ts
+++ b/src/client/views/newlightbox/utils.ts
@@ -9,9 +9,18 @@ export interface IDocRequest {
}
export const fetchRecommendations = async (src: string, query: string, docs?: IDocRequest[], dummy?: boolean) => {
- console.log("making request with: ", query)
+ console.log("[rec] making request")
if (dummy) {
- return dummyRecs;
+ return {
+ "recommendations": dummyRecs,
+ "keywords": dummyKeywords,
+ "num_recommendations": 4,
+ "max_x": 100,
+ "max_y": 100,
+ "min_x": 0,
+ "min_y": 0
+
+ };
}
const response = await fetch('http://127.0.0.1:8000/recommend', {
method: 'POST',
@@ -31,9 +40,11 @@ export const fetchRecommendations = async (src: string, query: string, docs?: ID
}
export const fetchKeywords = async (text: string, n: number, dummy?: boolean) => {
- console.log("making request with: ", text)
+ console.log("[fetchKeywords]")
if (dummy) {
- return dummyKeywords;
+ return {
+ "keywords": dummyKeywords
+ };
}
const response = await fetch('http://127.0.0.1:8000/keywords', {
method: 'POST',
@@ -69,8 +80,8 @@ export const getType = (type: DocumentType | string) => {
}
}
-const dummyRecs: IRecommendation[] = [
- {
+const dummyRecs = {
+ "a": {
title: 'Vannevar Bush - American Engineer',
previewUrl: 'https://cdn.britannica.com/98/23598-004-1E6A382E/Vannevar-Bush-Differential-Analyzer-1935.jpg',
type: 'web',
@@ -82,14 +93,14 @@ const dummyRecs: IRecommendation[] = [
y: 0
}
},
- {
+ "b": {
title: "From Memex to hypertext: Vannevar Bush and the mind's machine",
type: 'pdf',
distance: 5.4,
source: 'Google Scholar',
related_concepts: ['memex', 'vannevar bush', 'hypertext'],
},
- {
+ "c": {
title: 'How the hyperlink changed everything | Small Thing Big Idea, a TED series',
previewUrl: 'https://pi.tedcdn.com/r/talkstar-photos.s3.amazonaws.com/uploads/b17d043f-2642-4117-a913-52204505513f/MargaretGouldStewart_2018V-embed.jpg?u%5Br%5D=2&u%5Bs%5D=0.5&u%5Ba%5D=0.8&u%5Bt%5D=0.03&quality=82w=640',
type: 'youtube',
@@ -97,7 +108,7 @@ const dummyRecs: IRecommendation[] = [
source: 'www.youtube.com',
related_concepts: ['User Control', 'Explanations']
},
- {
+ "d": {
title: 'Recommender Systems: Behind the Scenes of Machine Learning-Based Personalization',
previewUrl: 'https://sloanreview.mit.edu/wp-content/uploads/2018/10/MAG-Ransbotham-Ratings-Recommendations-1200X627-1200x627.jpg',
type: 'pdf',
@@ -105,6 +116,6 @@ const dummyRecs: IRecommendation[] = [
source: 'www.altexsoft.com',
related_concepts: ['User Control', 'Explanations']
}
-]
+}
const dummyKeywords = ['user control', 'vannevar bush', 'hypermedia', 'hypertext'] \ No newline at end of file
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index e8865b918..5516cf205 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -9,16 +9,21 @@
background: none;
z-index: 1000;
padding: 0px;
+ overflow: scroll;
cursor: default;
.searchBox-bar {
width: 100%;
- height: 35px;
+ height: fit-content;
display: flex;
justify-content: center;
align-items: center;
background-color: none;
padding: 5px;
+ top: 0px;
+ position: sticky;
+ background: $light-gray;
+ border-bottom: $standard-border;
.searchBox-type {
display: block;
@@ -42,34 +47,66 @@
}
}
- .searchBox-results-container {
+ .section-header {
+
+ .section-title {
+ font-size: $body-text;
+ font-weight: 600;
+ }
+
+ .section-subtitle {
+ display: flex;
+ color: $light-gray;
+ }
+
+ padding: 5px 10px;
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ background: $medium-blue;
+ color: white;
+ }
+
+ .searchBox-recommendations-container {
display: flex;
flex-direction: column;
width: 100%;
- height: 100%;
+ height: fit-content;
justify-content: "center";
-
- .searchBox-results-count {
+
+ .searchBox-recommendations-view {
+ margin-top: 10px;
display: flex;
- color: gray;
- margin-left: 5px;
+ width: 100%;
+ height: fit-content;
+ flex-direction: column;
+ gap: 10px;
+ padding: 0px 10px;
+
+
}
+ }
+
+ .searchBox-results-container {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: fit-content;
+ justify-content: "center";
- .searchBox-results-scroll-view {
- margin-top: 10px;
+ .searchBox-results-view {
display: inline-block;
width: 100%;
- height: calc(100% - 55px);
- overflow-y: scroll;
+ height: fit-content;
.searchBox-results-scroll-view-result {
display: inline-block;
vertical-align: middle;
width: 100%;
- height: 50px;
+ height: fit-content;
cursor: pointer;
font-size: 15px;
- padding: 11px;
+ padding: 10px;
&.searchBox-results-scroll-view-result-selected {
background: #999;
@@ -81,6 +118,8 @@
width: calc(100% - 45px);
text-align: left;
overflow: hidden;
+ max-height: 2.4em;
+ line-height: 1.2em;
text-overflow: ellipsis;
}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3479cd20f..43e5344b6 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -14,6 +14,8 @@ import { CollectionDockingView } from '../collections/CollectionDockingView';
import { ViewBoxBaseComponent } from '../DocComponent';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import './SearchBox.scss';
+import { fetchRecommendations } from '../newlightbox/utils';
+import { IRecommendation, Recommendation } from '../newlightbox/components';
const DAMPENING_FACTOR = 0.9;
const MAX_ITERATIONS = 25;
@@ -42,6 +44,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
@observable _searchString = '';
@observable _docTypeString = 'all';
@observable _results: Map<Doc, string[]> = new Map<Doc, string[]>();
+ @observable _recommendations: IRecommendation[] = [];
@observable _pageRanks: Map<Doc, number> = new Map<Doc, number>();
@observable _linkedDocsOut: Map<Doc, Set<Doc>> = new Map<Doc, Set<Doc>>();
@observable _linkedDocsIn: Map<Doc, Set<Doc>> = new Map<Doc, Set<Doc>>();
@@ -393,6 +396,38 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
if (query) {
this.searchCollection(query);
+ const response = await fetchRecommendations('', query, [], true)
+ const recs = response.recommendations
+ const recommendations:IRecommendation[] = []
+ for (const key in recs) {
+ const title = recs[key].title;
+ console.log(title);
+ const url = recs[key].url
+ const type = recs[key].type
+ const text = recs[key].text
+ const transcript = recs[key].transcript
+ const previewUrl = recs[key].previewUrl
+ const embedding = recs[key].embedding
+ const distance = recs[key].distance
+ const source = recs[key].source
+ const related_concepts = recs[key].related_concepts
+ const docId = recs[key].doc_id
+ recommendations.push({
+ title: title,
+ data: url,
+ type: type,
+ text: text,
+ transcript: transcript,
+ previewUrl: previewUrl,
+ embedding: embedding,
+ distance: Math.round(distance * 100) / 100,
+ source: source,
+ related_concepts: related_concepts,
+ docId: docId
+ })
+ }
+ const setRecommendations = action(() => this._recommendations = recommendations)
+ setRecommendations()
}
};
@@ -487,6 +522,10 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
}
});
+ const recommendationsJSX: JSX.Element[] = this._recommendations.map((props) => (
+ <Recommendation {...props}/>
+ ))
+
return (
<div style={{ pointerEvents: 'all' }} className="searchBox-container">
<div className="searchBox-bar">
@@ -511,10 +550,20 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
ref={this._inputRef}
/>
</div>
- <div className="searchBox-results-container">
- <div className="searchBox-results-count">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
- <div className="searchBox-results-scroll-view">{resultsJSX}</div>
- </div>
+ {resultsJSX.length > 0 && <div className="searchBox-results-container">
+ <div className="section-header">
+ <div className="section-title">Results</div>
+ <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ </div>
+ <div className="searchBox-results-view">{resultsJSX}</div>
+ </div>}
+ {recommendationsJSX.length > 0 && <div className="searchBox-recommendations-container">
+ <div className="section-header">
+ <div className="section-title">Recommendations</div>
+ <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+ </div>
+ <div className="searchBox-recommendations-view">{recommendationsJSX}</div>
+ </div>}
</div>
);
}