Skip to content

Commit

Permalink
Small fix (#67)
Browse files Browse the repository at this point in the history
* fixed click link behaviour
* fixed openProject issues
* fixed the table dragging issue
* updated readme
  • Loading branch information
HuntFeng authored Sep 1, 2023
1 parent bc88f3a commit 917b96c
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 111 deletions.
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,28 @@

Research helper is a paper/book management tool. It is a reference management tool with various useful functionalities such as: built-in PDF reader, live markdown note and excalidraw note.

![screenshot.png](./galleries/screenshot.png)
## Reference Management

References can be managed in library page.
- Favorites, folders, tags, search, etc. All essential functions are there
- Able to one-click-retrieve meta information and related reference by providing identifier such as DOI
![library-page.png](./galleries/library-page.png)

## PDF Reader

Research helper has a built-in PDF reader.
- Able to adjust page layout, light/dark mode and more
- Support markdown comment (also latex) in annotations
- Able to preview internal links in hover windows
![reader-page.png](./galleries/reader-page.png)

## Note-taking System

Due to the flexible multi-window layout, it is never this easy to take notes while reading.
- Support WYSIWYG markdown note and excalidraw note
- Able to cite other references/notes in markdown note
- Support math(latex), code block, mindmap and more in markdown down
![note-page.png](./galleries/note-page.png)

# Contribute to Research-Helper

Expand All @@ -42,7 +63,19 @@ npm install

```bash
yarn dev # start electron app in development mode
yarn build # build the app with debugger
```

## To build the app
```bash
yarn build # build the app
# or
yarn debug # build the app with debugger
```

## Tests
```bash
yarn test:unit:ci # backend unit test
yarn test:component:ci # vue component test
```

# Acknowledgement
Expand Down
42 changes: 39 additions & 3 deletions README.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,31 @@ style="width: 100px; vertical-align:middle">

研究小助手是一款开源的文献管理工具。它有着内置的 PDF 阅读器,所见即所得的 Markdown 编辑器以及 Excalidraw 画板。你可以很好地用它来管理自己的电子书,文献以及笔记。

![screenshot.png](./galleries/screenshot.png)
## 文献管理

你可以在书库页面管理你的文献。

- 收藏、分类、标签、搜索,基本功能一应俱全
- 能根据 DOI 等信息一键获取该文献的详细信息以及相关文献
![library-page.png](./galleries/library-page.png)

## 文献阅读

自带的 PDF 阅读器能够让你浏览你的文献。

- 可调节页面布局、明暗模式、全屏模式等
- 注释支持 markdown 格式,以及 latex 语法
- 内部链接可在悬浮窗预览,轻松查看公式与图表
![reader-page.png](./galleries/reader-page.png)

## 笔记系统

得益于多窗口布局,阅读文献的同时记笔记是如此轻松

- 笔记分为所见即所得的 markdown 笔记以及自由画板的 excalidraw 笔记两种类型
- markdown 笔记能轻松引用别的文献或笔记
- markdown 笔记支持数学公式、代码块、思维导图等的渲染
![note-page.png](./galleries/note-page.png)

# 为研究小助手作贡献

Expand All @@ -42,8 +66,20 @@ npm install
## 开始开发吧

```bash
yarn dev # 开发模式
yarn build # 编译测试版
yarn dev # electron 开发模式
```

## 编译
```bash
yarn build # 编译发行版
# 或者
yarn debug # 编译带着debugger的测试版
```

## 测试
```bash
yarn test:unit:ci # 后端单元测试
yarn test:component:ci # 前端Vue Component测试
```

# 致谢
Expand Down
Binary file added galleries/library-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added galleries/note-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added galleries/reader-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed galleries/screenshot.png
Binary file not shown.
1 change: 1 addition & 0 deletions src/backend/appState/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ async function getAppState(): Promise<AppState> {
language: "en_US",
storagePath: "",
fontSize: "16px",
citeKeyRule: "author-title-year",
},
};
await db.put(state);
Expand Down
6 changes: 3 additions & 3 deletions src/components/WelcomeCarousel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@
import { ref, computed } from "vue";
import { useStateStore } from "src/stores/appState";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n({ useScope: "global" });
const { locale } = useI18n({ useScope: "global" });
const props = defineProps({ modelValue: { type: Boolean, required: true } });
const emit = defineEmits(["update:modelValue", "updateAppState"]);
const emit = defineEmits(["update:modelValue"]);
const stateStore = useStateStore();
Expand Down Expand Up @@ -99,7 +99,7 @@ function changeStoragePath() {
if (result !== undefined && !!result[0]) {
path.value = result[0];
stateStore.settings.storagePath = path.value;
emit("updateAppState");
stateStore.saveAppState();
}
}
Expand Down
42 changes: 4 additions & 38 deletions src/components/leftmenu/ProjectTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -150,58 +150,28 @@
</q-tree>
</template>
<script setup lang="ts">
import { nextTick, onMounted, ref, watch } from "vue";
import { nextTick, ref, watchEffect } from "vue";
import { QTree } from "quasar";
import { Note, NoteType, Page, Project } from "src/backend/database";
import { Note, NoteType, Project } from "src/backend/database";
// db
import { useStateStore } from "src/stores/appState";
import { useProjectStore } from "src/stores/projectStore";
import { getProject } from "src/backend/project/project";
const stateStore = useStateStore();
const projectStore = useProjectStore();
const tree = ref<QTree | null>(null);
const renameInput = ref<HTMLInputElement | null>(null);
// const renamingNote = ref<QTreeNode | null>(null);
const renamingNoteId = ref("");
const addingNote = ref(false);
const expanded = ref<string[]>([]);
const showProjectMenu = ref(true);
onMounted(async () => {
watchEffect(() => {
// expand all projects
expanded.value = Array.from(projectStore.openedProjects.map((p) => p._id));
// select the item associated with current window
let selected = stateStore.currentPageId;
if (!tree.value) return;
let selectedNode = tree.value.getNodeByKey(selected);
if (!!selectedNode && selectedNode?.children?.length > 0)
expanded.value.push(selected);
expanded.value = projectStore.openedProjects.map((p) => p._id);
});
watch(
() => stateStore.openedPage,
async (page: Page) => {
if (page.type.indexOf("Plugin") > -1) return;
if (!!!page.id || !tree.value) return;
let node = tree.value.getNodeByKey(page.id);
if (!!node) return; // if project is active already, return
let item = (await getProject(page.id)) as Project | Note;
if (item?.dataType == "project") {
await projectStore.openProject(page.id);
expanded.value.push(page.id);
} else if (item?.dataType == "note") {
// some notes are independent of project, like memo
if (!item.projectId) return;
await projectStore.openProject(item.projectId);
}
},
{ deep: true }
);
function menuSwitch(node: Project | Note) {
if (node.dataType == "note") {
// show context menu for notes
Expand All @@ -214,10 +184,6 @@ function menuSwitch(node: Project | Note) {
function selectItem(node: Project | Note) {
console.log(node);
stateStore.currentPageId = node._id;
if (node.dataType === "project" && (node.children?.length as number) > 0)
expanded.value.push(node._id);
// open item
let id = node._id;
let type = "";
Expand Down
9 changes: 8 additions & 1 deletion src/components/library/ProjectTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@
@dragend="onDragEnd"
@expandRow="(isExpand: boolean) => props.expand=isExpand"
@mousedown="(e: PointerEvent) => clickProject(props, e)"
@mouseup="(e: PointerEvent) => clickProject(props, e)"
@dblclick="dblclickProject(props.row)"
@contextmenu="(e:PointerEvent) => toggleContextMenu(props, e)"
/>

<!-- Expanded Rows -->

<!-- PDF -->
Expand Down Expand Up @@ -223,6 +223,13 @@ function clickProject(
e: PointerEvent
) {
if (e.button !== 0) return; // return if not left click
if (e.type === "mousedown" && props.selected) return; // return if clicking on a selected project
if (
e.type === "mouseup" &&
(e.target as HTMLInputElement)?.type === "checkbox"
)
return; // return if clicking on the checkbox, the checkbox will take care of selection for us
// row: Project, rowIndex: number
let row = props.row;
let descriptor = Object.getOwnPropertyDescriptor(props, "selected");
Expand Down
9 changes: 6 additions & 3 deletions src/components/note/NoteEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -266,17 +266,20 @@ function _changeLinks() {
"[data-type='a']"
) as NodeListOf<HTMLElement>;
for (let linkNode of linkNodes) {
linkNode.onclick = () => clickLink(linkNode);
linkNode.onclick = (e) => clickLink(e, linkNode);
linkNode.onmouseover = () => hoverLink(linkNode);
}
}
const changeLinks = debounce(_changeLinks, 50) as () => void;
async function clickLink(linkNode: HTMLElement) {
async function clickLink(e: MouseEvent, linkNode: HTMLElement) {
if (!vditor.value) return;
e.stopImmediatePropagation(); // stop propagating the click event
vditor.value.blur(); // save the content before jumping
let link = linkNode.innerText;
let link = (
linkNode.querySelector("span.vditor-ir__marker--link") as HTMLElement
).innerText;
try {
// valid external url, open it externally
new URL(link);
Expand Down
12 changes: 4 additions & 8 deletions src/components/pdfreader/PeekCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
</q-card>
</template>
<script setup lang="ts">
import { nextTick, onMounted, ref, PropType, watch } from "vue";
import { nextTick, onMounted, ref, PropType, watch, watchEffect } from "vue";
import * as pdfjsLib from "pdfjs-dist";
import * as pdfjsViewer from "pdfjs-dist/web/pdf_viewer";
Expand All @@ -66,10 +66,9 @@ const card = ref();
const peekContainer = ref();
const pinned = ref(false);
watch(
() => props.darkMode,
(darkMode) => setViewMode(darkMode)
);
watchEffect(() => {
setViewMode(props.darkMode);
});
onMounted(() => {
if (!peekContainer.value || !props.peekManager.pdfDocument) return;
Expand Down Expand Up @@ -119,9 +118,6 @@ onMounted(() => {
// show peeker
show();
// set view mode
setViewMode(props.darkMode);
});
/**
Expand Down
48 changes: 6 additions & 42 deletions src/layouts/AppLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,18 @@
<WelcomeCarousel
v-else-if="showWelcomeCarousel"
v-model="showWelcomeCarousel"
@updateAppState="saveAppState"
/>
<MainLayout
v-else
@updateAppState="saveAppState"
/>
<MainLayout v-else />
</template>
<script setup lang="ts">
import WelcomeCarousel from "src/components/WelcomeCarousel.vue";
import MainLayout from "./MainLayout.vue";
import { useQuasar } from "quasar";
import { getAppState, updateAppState } from "src/backend/appState";
import { getAppState } from "src/backend/appState";
import { useStateStore } from "src/stores/appState";
import { useProjectStore } from "src/stores/projectStore";
import { onMounted, ref } from "vue";
import { useI18n } from "vue-i18n";
const $q = useQuasar();
const { locale } = useI18n({ useScope: "global" });
const stateStore = useStateStore();
const projectStore = useProjectStore();
Expand All @@ -65,39 +59,9 @@ onMounted(async () => {
}
// apply settings
changeTheme(stateStore.settings.theme);
changeLanguage(stateStore.settings.language);
changeFontSize(stateStore.settings.fontSize);
stateStore.changeTheme(stateStore.settings.theme);
stateStore.changeFontSize(parseFloat(stateStore.settings.fontSize));
stateStore.changeLanguage(stateStore.settings.language);
locale.value = stateStore.settings.language;
});
/***************************
* Load and apply settings
***************************/
function changeTheme(theme: string) {
switch (theme) {
case "dark":
$q.dark.set(true);
break;
case "light":
$q.dark.set(false);
break;
}
}
function changeLanguage(language: string) {
locale.value = language;
}
function changeFontSize(fontSize: string) {
document.documentElement.style.fontSize = fontSize;
}
async function saveAppState() {
let state = stateStore.saveState();
state.openedProjectIds = projectStore.openedProjects.map(
(project) => project._id
);
await updateAppState(state);
}
</script>
Loading

0 comments on commit 917b96c

Please sign in to comment.