aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/KeyphraseQueryView.scss8
-rw-r--r--src/client/views/KeyphraseQueryView.tsx35
-rw-r--r--src/client/views/MainView.tsx7
-rw-r--r--src/client/views/RecommendationsBox.scss68
-rw-r--r--src/client/views/RecommendationsBox.tsx199
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx13
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx3
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx3
-rw-r--r--src/client/views/nodes/DocumentView.tsx135
-rw-r--r--src/client/views/nodes/ImageBox.tsx7
12 files changed, 474 insertions, 8 deletions
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 979687ffb..65d327392 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -6,6 +6,7 @@ import { DragManager } from "../util/DragManager";
import { action, runInAction } from "mobx";
import { Doc } from "../../new_fields/Doc";
import { DictationManager } from "../util/DictationManager";
+import { RecommendationsBox } from "./RecommendationsBox";
import SharingManager from "../util/SharingManager";
import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
import { Cast, PromiseValue } from "../../new_fields/Types";
@@ -79,6 +80,7 @@ export default class KeyManager {
}
SelectionManager.DeselectAll();
DictationManager.Controls.stop();
+ // RecommendationsBox.Instance.closeMenu();
SharingManager.Instance.close();
break;
case "delete":
diff --git a/src/client/views/KeyphraseQueryView.scss b/src/client/views/KeyphraseQueryView.scss
new file mode 100644
index 000000000..ac715e5e7
--- /dev/null
+++ b/src/client/views/KeyphraseQueryView.scss
@@ -0,0 +1,8 @@
+.fading {
+ animation: fanOut 1s
+}
+
+@keyframes fanOut {
+ from {opacity: 0;}
+ to {opacity: 1;}
+} \ No newline at end of file
diff --git a/src/client/views/KeyphraseQueryView.tsx b/src/client/views/KeyphraseQueryView.tsx
new file mode 100644
index 000000000..a9dafc4a4
--- /dev/null
+++ b/src/client/views/KeyphraseQueryView.tsx
@@ -0,0 +1,35 @@
+import { observer } from "mobx-react";
+import React = require("react");
+import "./KeyphraseQueryView.scss";
+
+// tslint:disable-next-line: class-name
+export interface KP_Props {
+ keyphrases: string;
+}
+
+@observer
+export class KeyphraseQueryView extends React.Component<KP_Props>{
+ constructor(props: KP_Props) {
+ super(props);
+ console.log("FIRST KEY PHRASE: ", props.keyphrases[0]);
+ }
+
+ render() {
+ let kps = this.props.keyphrases.toString();
+ let keyterms = this.props.keyphrases.split(',');
+ return (
+ <div>
+ <h5>Select queries to send:</h5>
+ <form>
+ {keyterms.map((kp: string) => {
+ //return (<p>{"-" + kp}</p>);
+ return (<p><label>
+ <input name="query" type="radio" />
+ <span>{kp}</span>
+ </label></p>);
+ })}
+ </form>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4e81f528a..92b26b9bc 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -32,9 +32,14 @@ import KeyManager from './GlobalKeyHandler';
import "./MainView.scss";
import { MainViewNotifs } from './MainViewNotifs';
import { DocumentView } from './nodes/DocumentView';
-import { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
+import { FilterBox } from './search/FilterBox';
+import { SchemaHeaderField, RandomPastel } from '../../new_fields/SchemaHeaderField';
+//import { DocumentManager } from '../util/DocumentManager';
+import { RecommendationsBox } from './RecommendationsBox';
+import { PresBox } from './nodes/PresBox';
+import { OverlayView } from './OverlayView';
import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu';
import GestureOverlay from './GestureOverlay';
import { Scripting } from '../util/Scripting';
diff --git a/src/client/views/RecommendationsBox.scss b/src/client/views/RecommendationsBox.scss
new file mode 100644
index 000000000..dd8a105f6
--- /dev/null
+++ b/src/client/views/RecommendationsBox.scss
@@ -0,0 +1,68 @@
+@import "globalCssVariables";
+
+.rec-content *{
+ display: inline-block;
+ margin: auto;
+ width: 50;
+ height: 150px;
+ border: 1px dashed grey;
+ padding: 10px 10px;
+}
+
+.rec-content {
+ float: left;
+ width: inherit;
+ align-content: center;
+}
+
+.rec-scroll {
+ overflow-y: scroll;
+ overflow-x: hidden;
+ position: absolute;
+ pointer-events: all;
+ // display: flex;
+ z-index: 10000;
+ box-shadow: gray 0.2vw 0.2vw 0.4vw;
+ // flex-direction: column;
+ background: whitesmoke;
+ padding-bottom: 10px;
+ padding-top: 20px;
+ // border-radius: 15px;
+ border: solid #BBBBBBBB 1px;
+ width: 100%;
+ text-align: center;
+ // max-height: 250px;
+ height: 100%;
+ text-transform: uppercase;
+ color: grey;
+ letter-spacing: 2px;
+}
+
+.content {
+ padding: 10px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+.image-background {
+ pointer-events: none;
+ background-color: transparent;
+ width: 50%;
+ text-align: center;
+ margin-left: 5px;
+}
+
+img{
+ width: 100%;
+ height: 100%;
+}
+
+.score {
+ // margin-left: 15px;
+ width: 50%;
+ height: 100%;
+ text-align: center;
+ margin-left: 10px;
+}
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
new file mode 100644
index 000000000..0e3cfd729
--- /dev/null
+++ b/src/client/views/RecommendationsBox.tsx
@@ -0,0 +1,199 @@
+import { observer } from "mobx-react";
+import React = require("react");
+import { observable, action, computed, runInAction } from "mobx";
+import Measure from "react-measure";
+import "./RecommendationsBox.scss";
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc";
+import { DocumentIcon } from "./nodes/DocumentIcon";
+import { StrCast, NumCast } from "../../new_fields/Types";
+import { returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../Utils";
+import { Transform } from "../util/Transform";
+import { ObjectField } from "../../new_fields/ObjectField";
+import { DocumentView } from "./nodes/DocumentView";
+import { DocumentType } from '../documents/DocumentTypes';
+import { ClientRecommender } from "../ClientRecommender";
+import { DocServer } from "../DocServer";
+import { Id } from "../../new_fields/FieldSymbols";
+import { FieldView, FieldViewProps } from "./nodes/FieldView";
+import { DocumentManager } from "../util/DocumentManager";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { library } from "@fortawesome/fontawesome-svg-core";
+import { faBullseye, faLink } from "@fortawesome/free-solid-svg-icons";
+import { DocUtils } from "../documents/Documents";
+
+export interface RecProps {
+ documents: { preview: Doc, similarity: number }[];
+ node: Doc;
+}
+
+library.add(faBullseye, faLink);
+
+@observer
+export class RecommendationsBox extends React.Component<FieldViewProps> {
+
+ public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(RecommendationsBox, fieldKey); }
+
+ // @observable private _display: boolean = false;
+ @observable private _pageX: number = 0;
+ @observable private _pageY: number = 0;
+ @observable private _width: number = 0;
+ @observable private _height: number = 0;
+ @observable.shallow private _docViews: JSX.Element[] = [];
+ // @observable private _documents: { preview: Doc, score: number }[] = [];
+ private previewDocs: Doc[] = [];
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ }
+
+ @action
+ private DocumentIcon(doc: Doc) {
+ let layoutresult = StrCast(doc.type);
+ let renderDoc = doc;
+ //let box: number[] = [];
+ if (layoutresult.indexOf(DocumentType.COL) !== -1) {
+ renderDoc = Doc.MakeDelegate(renderDoc);
+ }
+ let returnXDimension = () => 150;
+ let returnYDimension = () => 150;
+ let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
+ //let scale = () => 1;
+ let newRenderDoc = Doc.MakeAlias(renderDoc); /// newRenderDoc -> renderDoc -> render"data"Doc -> TextProt
+ newRenderDoc.height = NumCast(this.props.Document.documentIconHeight);
+ newRenderDoc.autoHeight = false;
+ const docview = <div>
+ <DocumentView
+ fitToBox={StrCast(doc.type).indexOf(DocumentType.COL) !== -1}
+ Document={newRenderDoc}
+ addDocument={returnFalse}
+ removeDocument={returnFalse}
+ ruleProvider={undefined}
+ ScreenToLocalTransform={Transform.Identity}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ renderDepth={1}
+ PanelWidth={returnXDimension}
+ PanelHeight={returnYDimension}
+ focus={emptyFunction}
+ backgroundColor={returnEmptyString}
+ parentActive={returnFalse}
+ whenActiveChanged={returnFalse}
+ bringToFront={emptyFunction}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ ContentScaling={scale}
+ />
+ </div>;
+ return docview;
+
+ }
+
+ // @action
+ // closeMenu = () => {
+ // this._display = false;
+ // this.previewDocs.forEach(doc => DocServer.DeleteDocument(doc[Id]));
+ // this.previewDocs = [];
+ // }
+
+ // @action
+ // resetDocuments = () => {
+ // this._documents = [];
+ // }
+
+ // @action
+ // displayRecommendations(x: number, y: number) {
+ // this._pageX = x;
+ // this._pageY = y;
+ // this._display = true;
+ // }
+
+ static readonly buffer = 20;
+
+ // get pageX() {
+ // const x = this._pageX;
+ // if (x < 0) {
+ // return 0;
+ // }
+ // const width = this._width;
+ // if (x + width > window.innerWidth - RecommendationsBox.buffer) {
+ // return window.innerWidth - RecommendationsBox.buffer - width;
+ // }
+ // return x;
+ // }
+
+ // get pageY() {
+ // const y = this._pageY;
+ // if (y < 0) {
+ // return 0;
+ // }
+ // const height = this._height;
+ // if (y + height > window.innerHeight - RecommendationsBox.buffer) {
+ // return window.innerHeight - RecommendationsBox.buffer - height;
+ // }
+ // return y;
+ // }
+
+ // get createDocViews() {
+ // return DocListCast(this.props.Document.data).map(doc => {
+ // return (
+ // <div className="content">
+ // <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
+ // {this.DocumentIcon(doc)}
+ // </span>
+ // <span className="score">{NumCast(doc.score).toFixed(4)}</span>
+ // <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
+ // <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
+ // </div>
+ // <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
+ // <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
+ // </div>
+ // </div>
+ // );
+ // });
+ // }
+
+ componentDidMount() { //TODO: invoking a computedFn from outside an reactive context won't be memoized, unless keepAlive is set
+ runInAction(() => {
+ if (this._docViews.length === 0) {
+ this._docViews = DocListCast(this.props.Document.data).map(doc => {
+ return (
+ <div className="content">
+ <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
+ {this.DocumentIcon(doc)}
+ </span>
+ <span className="score">{NumCast(doc.score).toFixed(4)}</span>
+ <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
+ </div>
+ <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
+ </div>
+ </div>
+ );
+ });
+ }
+ });
+ }
+
+ render() { //TODO: Invariant violation: max depth exceeded error. Occurs when images are rendered.
+ // if (!this._display) {
+ // return null;
+ // }
+ // let style = { left: this.pageX, top: this.pageY };
+ //const transform = "translate(" + (NumCast(this.props.node.x) + 350) + "px, " + NumCast(this.props.node.y) + "px"
+ let title = StrCast((this.props.Document.sourceDoc as Doc).title);
+ if (title.length > 15) {
+ title = title.substring(0, 15) + "...";
+ }
+ return (
+ <div className="rec-scroll">
+ <p>Recommendations for "{title}"</p>
+ {this._docViews}
+ </div>
+ );
+ }
+ //
+ //
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index caffa7eb1..4eba5dc26 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -23,6 +23,7 @@ import { faExpand } from '@fortawesome/free-solid-svg-icons';
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { KeyCodes } from "../../northstar/utils/KeyCodes";
import { undoBatch } from "../../util/UndoManager";
+import { List } from "lodash";
library.add(faExpand);
@@ -82,10 +83,20 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
}
@action
- onPointerDown = (e: React.PointerEvent): void => {
+ onPointerDown = async (e: React.PointerEvent): Promise<void> => {
this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
this.props.setPreviewDoc(this.props.rowProps.original);
+ let url: string;
+ if (url = StrCast(this.props.rowProps.row.href)) {
+ try {
+ new URL(url);
+ const temp = window.open(url)!;
+ temp.blur();
+ window.focus();
+ } catch { }
+ }
+
// this._isEditing = true;
// this.props.setIsEditing(true);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 517f467b7..414bbfc0b 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -277,7 +277,7 @@
display:flex;
flex-direction: row;
width: 150px;
- margin: auto 0 auto auto;
+ margin: auto auto auto auto;
}
.react-autosuggest__container {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 099ad69d3..53fe2b18c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -27,7 +27,7 @@ import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
import { InkingControl } from "../../InkingControl";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocumentViewProps } from "../../nodes/DocumentView";
+import { DocumentContentsView } from "../../nodes/DocumentContentsView";
import { FormattedTextBox } from "../../nodes/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
import PDFMenu from "../../pdf/PDFMenu";
@@ -44,6 +44,7 @@ import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { RichTextField } from "../../../../new_fields/RichTextField";
import { List } from "../../../../new_fields/List";
+import { DocumentViewProps } from "../../nodes/DocumentView";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 0b01e6471..3b1a86d32 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -33,6 +33,7 @@ import { VideoBox } from "./VideoBox";
import { WebBox } from "./WebBox";
import { InkingStroke } from "../InkingStroke";
import React = require("react");
+import { RecommendationsBox } from "../RecommendationsBox";
import { TraceMobx } from "../../../new_fields/util";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -102,7 +103,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FontIconBox: FontIconBox, ButtonBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox, QueryBox,
- ColorBox, DocuLinkBox, InkingStroke, DocumentBox
+ ColorBox, DocuLinkBox, InkingStroke, DocumentBox, RecommendationsBox
}}
bindings={this.CreateBindings()}
jsx={this.layout}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4a9a6c867..765077c4d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, runInAction, trace } from "mobx";
+import { action, computed, runInAction, trace, observable } from "mobx";
import { observer } from "mobx-react";
import * as rp from "request-promise";
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
@@ -50,6 +50,9 @@ import { RadialMenuProps } from './RadialMenuItem';
import { CollectionStackingView } from '../collections/CollectionStackingView';
import { RichTextField } from '../../../new_fields/RichTextField';
+import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
+import { ClientRecommender } from '../../ClientRecommender';
+import { SearchUtil } from '../../util/SearchUtil';
library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -99,6 +102,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _hitTemplateDrag = false;
private _mainCont = React.createRef<HTMLDivElement>();
private _dropDisposer?: DragManager.DragDropDisposer;
+ private _showKPQuery: boolean = false;
+ private _queries: string = "";
private _gestureEventDisposer?: GestureUtils.GestureEventDisposer;
private _titleRef = React.createRef<EditableView>();
@@ -793,6 +798,35 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// a.download = `DocExport-${this.props.Document[Id]}.zip`;
// a.click();
});
+ let recommender_subitems: ContextMenuProps[] = [];
+
+ recommender_subitems.push({
+ description: "Internal recommendations",
+ event: () => this.recommender(e),
+ icon: "brain"
+ });
+
+ let ext_recommender_subitems: ContextMenuProps[] = [];
+
+ ext_recommender_subitems.push({
+ description: "arXiv",
+ event: () => this.externalRecommendation(e, "arxiv"),
+ icon: "brain"
+ });
+ ext_recommender_subitems.push({
+ description: "Bing",
+ event: () => this.externalRecommendation(e, "bing"),
+ icon: "brain"
+ });
+
+ recommender_subitems.push({
+ description: "External recommendations",
+ subitems: ext_recommender_subitems,
+ icon: "brain"
+ });
+
+ cm.addItem({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" });
+
moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
@@ -847,6 +881,104 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
+ recommender = async (e: React.MouseEvent) => {
+ if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" });
+ const documents: Doc[] = [];
+ const allDocs = await SearchUtil.GetAllDocs();
+ // allDocs.forEach(doc => console.log(doc.title));
+ // clears internal representation of documents as vectors
+ ClientRecommender.Instance.reset_docs();
+ //ClientRecommender.Instance.arxivrequest("electrons");
+ await Promise.all(allDocs.map((doc: Doc) => {
+ let isMainDoc: boolean = false;
+ const dataDoc = Doc.GetDataDoc(doc);
+ if (doc.type === DocumentType.TEXT) {
+ if (dataDoc === Doc.GetDataDoc(this.props.Document)) {
+ isMainDoc = true;
+ }
+ if (!documents.includes(dataDoc)) {
+ documents.push(dataDoc);
+ const extdoc = doc.data_ext as Doc;
+ return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, true, "", isMainDoc);
+ }
+ }
+ if (doc.type === DocumentType.IMG) {
+ if (dataDoc === Doc.GetDataDoc(this.props.Document)) {
+ isMainDoc = true;
+ }
+ if (!documents.includes(dataDoc)) {
+ documents.push(dataDoc);
+ const extdoc = doc.data_ext as Doc;
+ return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, true, "", isMainDoc, true);
+ }
+ }
+ }));
+ const doclist = ClientRecommender.Instance.computeSimilarities("cosine");
+ let recDocs: { preview: Doc, score: number }[] = [];
+ // tslint:disable-next-line: prefer-for-of
+ for (let i = 0; i < doclist.length; i++) {
+ recDocs.push({ preview: doclist[i].actualDoc, score: doclist[i].score });
+ }
+
+ const data = recDocs.map(unit => {
+ unit.preview.score = unit.score;
+ return unit.preview;
+ });
+
+ console.log(recDocs.map(doc => doc.score));
+
+ const title = `Showing ${data.length} recommendations for "${StrCast(this.props.Document.title)}"`;
+ const recommendations = Docs.Create.RecommendationsDocument(data, { title });
+ recommendations.documentIconHeight = 150;
+ recommendations.sourceDoc = this.props.Document;
+ recommendations.sourceDocContext = this.props.ContainingCollectionView!.props.Document;
+ CollectionDockingView.AddRightSplit(recommendations, undefined);
+
+ // RecommendationsBox.Instance.displayRecommendations(e.pageX + 100, e.pageY);
+ }
+
+ @action
+ externalRecommendation = async (e: React.MouseEvent, api: string) => {
+ if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" });
+ ClientRecommender.Instance.reset_docs();
+ const doc = Doc.GetDataDoc(this.props.Document);
+ const extdoc = doc.data_ext as Doc;
+ const recs_and_kps = await ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, false, api);
+ let recs: any;
+ let kps: any;
+ if (recs_and_kps) {
+ recs = recs_and_kps.recs;
+ kps = recs_and_kps.keyterms;
+ }
+ else {
+ console.log("recommender system failed :(");
+ return;
+ }
+ console.log("ibm keyterms: ", kps.toString());
+ const headers = [new SchemaHeaderField("title"), new SchemaHeaderField("href")];
+ const bodies: Doc[] = [];
+ const titles = recs.title_vals;
+ const urls = recs.url_vals;
+ for (let i = 0; i < 5; i++) {
+ const body = Docs.Create.FreeformDocument([], { title: titles[i] });
+ body.href = urls[i];
+ bodies.push(body);
+ }
+ CollectionDockingView.AddRightSplit(Docs.Create.SchemaDocument(headers, bodies, { title: `Showing External Recommendations for "${StrCast(doc.title)}"` }), undefined);
+ this._showKPQuery = true;
+ this._queries = kps.toString();
+ }
+
+ onPointerEnter = (e: React.PointerEvent): void => { Doc.BrushDoc(this.props.Document); };
+ onPointerLeave = (e: React.PointerEvent): void => { Doc.UnBrushDoc(this.props.Document); };
+
+ // the document containing the view layout information - will be the Document itself unless the Document has
+ // a layout field. In that case, all layout information comes from there unless overriden by Document
+ get layoutDoc(): Document {
+ return Document(Doc.Layout(this.props.Document));
+ }
+
+ // does Document set a layout prop
// does Document set a layout prop
setsLayoutProp = (prop: string) => this.props.Document[prop] !== this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)] && this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)];
// get the a layout prop by first choosing the prop from Document, then falling back to the layout doc otherwise.
@@ -1010,6 +1142,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}}>
{this.innards}
</div>;
+ { this._showKPQuery ? <KeyphraseQueryView keyphrases={this._queries}></KeyphraseQueryView> : undefined }
}
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 33c694c6e..207546936 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,6 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEye } from '@fortawesome/free-regular-svg-icons';
-import { faAsterisk, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons';
+import { faAsterisk, faFileAudio, faImage, faPaintBrush, faBrain } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction, trace } from 'mobx';
import { observer } from "mobx-react";
@@ -22,6 +22,8 @@ import FaceRectangles from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
+import { SearchUtil } from '../../util/SearchUtil';
+import { ClientRecommender } from '../../ClientRecommender';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { documentSchema } from '../../../new_fields/documentSchemas';
import { Id, Copy } from '../../../new_fields/FieldSymbols';
@@ -35,7 +37,7 @@ const path = require('path');
const { Howl } = require('howler');
-library.add(faImage, faEye as any, faPaintBrush);
+library.add(faImage, faEye as any, faPaintBrush, faBrain);
library.add(faFileAudio, faAsterisk);
@@ -163,6 +165,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
+ //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" });
!existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs, icon: "asterisk" });