GithubHelp home page GithubHelp logo

fantasticit / think Goto Github PK

View Code? Open in Web Editor NEW
1.8K 1.8K 346.0 7.33 MB

云策文档是一款开源知识管理工具。通过独立的知识库空间,结构化地组织在线协作文档,实现知识的积累与沉淀,促进知识的复用与流通。

Home Page: https://think.codingit.cn

License: MIT License

TypeScript 96.94% SCSS 3.00% Shell 0.04% Dockerfile 0.01%
collaborative-editing nestjs nextjs

think's Introduction

think's People

Contributors

code-lixm avatar fantasticit avatar hello-job avatar tinsfox avatar weikx avatar wuding129 avatar xinlc avatar yangfong avatar zhaotao1989 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

think's Issues

使用docker安装报错

npm ERR! code EAI_AGAIN
npm ERR! syscall getaddrinfo
npm ERR! errno EAI_AGAIN
npm ERR! request to https://registry.npm.taobao.org/@nestjs%2fcli failed, reason: getaddrinfo EAI_AGAIN registry.npm.taobao.org

npm ERR! A complete log of this run can be found in:
npm ERR! /root/.npm/_logs/2022-06-21T02_57_51_217Z-debug-0.log
The command '/bin/sh -c npm i -g pm2 @nestjs/cli pnpm' returned a non-zero code: 1
ERROR: Service 'thinkdoc' failed to build

另外:EIP设置ip应怎样设置

文档标题状态不同步

一、问题的描述

1. 问题的表现是怎样的?

  • 页面中标题全部删除后,左上方标题还会剩余一个字符
    BUG-TITLE-CASE1

  • 一次性删除全部标题后,左上方标题依然存在
    BUG-TITLE-CASE2

2. 问题的复现路径

  • 如上

3. 正常的预期是什么?

  • 页面左上角标题状态和文档中完全同步

如何部署

大神,这个部署必须使用docker部署吗?有离线安装包解压的部署吗?有具体部署步骤吗?

求助修改EIP不生效

根据 Docker-compose 一键构建安装 教程中 修改 docker-compose.yml 中的 EIP 参数
image

启动服务
docker-compose up -d
发现请求接口 是 x.x.x.x
image

请问是配置 docker-compose.yml EIP 参数还需要做些什么吗?

新建文档的按钮逻辑有点问题

新建文档的时候,
在公开模板内选择任意一个模板,
点击模板上弹出的使用按钮会自动选择该模板,
再点击确定的时候提示我“您不是模板创建者,无法编辑”,

然而当我点击模板上右上角的编辑图标时,可以正常使用,进入的却是空白页面而没有文档的内容。

好家伙进入文档编辑界面的时候,我想点右上角的删除,然后又提示我您不是模板创建者,无法删除...

目录分享

您好,我看咱们现在都是单文档分享
可以支持目录或整个知识库分享吗
这样的话,就更便捷了

文件:更好的文件处理方式

文件上传作为重要但非核心功能,云策文档不会开发太多,方案上仍旧采用开源(或付费)成熟方案,结合到内部形成多种方案,通过配置决定启用哪种方案。

产品功能点

前端

  • 文件上传不卡顿
  • 文件上传有进度
  • 重复文件秒传

后端

通过鉴权后,服务器做中转将文件上传到成熟方案上。

方案设计

前端

大文件切片上传,并结合 web-worker 文件 hash 计算。(see)。

后端

抽象 client 实现,结合实际实现 uploadFileuploadPartmergePart 接口。可采用的方案有 minioasw-sdk、七牛云、阿里云、京东云、腾讯云。

[BUG] Server Client aria-describedby不一致

1. 问题的表现是怎样的?

next报了服务端与客户端的id不一致,定位到了是packages/client/src/layouts/router-header/recent.tsx Line 29的问题,不过好像是semi的问题?辛苦排查一下
image

2. 问题的复现路径

http://localhost:5001/

3. 预期是什么?

No warning

ssr 能力

为了激活客户端的服务端渲染能力,需要:

  1. 服务端鉴权改为 set-cookie,客户端就能在 node 端拿到 cookie 进行预请求
  2. 通过 node 端预请求,客户端可以第一时间拿到数据进行页面渲染,同时结合第三方库仍可在 csr 阶段进行 focus、offline、background 的请求
  3. 客户端使用的 swr 不太合适了,可以切换到 react-query

可行 demo:

image

image

方案:using-hydration
分支:cookie-ssr

产品设计意见征求

think 最初的定位是个人使用,优先支持 markdown,可分享,可协作。随着关注人数的增多,大家可能都有不同的需求,为了下一步规划,欢迎大家提出相关意见。

出现滚动条时页面发生抖动

1. 问题的表现是怎样的?

编辑文档内容达到一定高度时页面会出现抖动
BUG-PAGE-SHIFT

2. 问题的复现路径

如上

3. 预期是什么?

文档始终保持同一位置

编辑器体验

复制粘贴

场景一:粘贴其他文档内容,应保留格式
场景二:复制粘贴纯文本
场景三:在文档编辑时,快速复制某一类型节点及其内容

关于授权

既然是MIT协议,为什么商用之前还要联系授权呢?

协作插件错误

如果出现插件报错,替换 node_modules 下 y-cursor 的代码。


import * as Y from 'yjs'
import { Decoration, DecorationSet } from 'prosemirror-view' // eslint-disable-line
import { Plugin } from 'prosemirror-state' // eslint-disable-line
import { Awareness } from 'y-protocols/awareness' // eslint-disable-line
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, setMeta } from '../lib.js'
import { yCursorPluginKey, ySyncPluginKey } from './keys.js'

import * as math from 'lib0/math'

/**
 * Default generator for a cursor element
 *
 * @param {any} user user data
 * @return HTMLElement
 */
export const defaultCursorBuilder = user => {
  const cursor = document.createElement('span')
  cursor.classList.add('ProseMirror-yjs-cursor')
  cursor.setAttribute('style', `border-color: ${user.color}`)
  const userDiv = document.createElement('div')
  userDiv.setAttribute('style', `background-color: ${user.color}`)
  userDiv.insertBefore(document.createTextNode(user.name), null)
  cursor.insertBefore(userDiv, null)
  return cursor
}

const rxValidColor = /^#[0-9a-fA-F]{6}$/

/**
 * @param {any} state
 * @param {Awareness} awareness
 * @return {any} DecorationSet
 */
export const createDecorations = (state, awareness, createCursor) => {
  const ystate = ySyncPluginKey.getState(state) ||  state['y-sync$'];
  const y = ystate.doc
  const decorations = []
  if (ystate.snapshot != null || ystate.prevSnapshot != null || ystate.binding === null) {
    // do not render cursors while snapshot is active
    return DecorationSet.create(state.doc, [])
  }
  awareness.getStates().forEach((aw, clientId) => {
    if (clientId === y.clientID) {
      return
    }
    if (aw.cursor != null) {
      const user = aw.user || {}
      if (user.color == null) {
        user.color = '#ffa500'
      } else if (!rxValidColor.test(user.color)) {
        // We only support 6-digit RGB colors in y-prosemirror
        console.warn('A user uses an unsupported color format', user)
      }
      if (user.name == null) {
        user.name = `User: ${clientId}`
      }
      let anchor = relativePositionToAbsolutePosition(y, ystate.type, Y.createRelativePositionFromJSON(aw.cursor.anchor), ystate.binding.mapping)
      let head = relativePositionToAbsolutePosition(y, ystate.type, Y.createRelativePositionFromJSON(aw.cursor.head), ystate.binding.mapping)
      if (anchor !== null && head !== null) {
        const maxsize = math.max(state.doc.content.size - 1, 0)
        anchor = math.min(anchor, maxsize)
        head = math.min(head, maxsize)
        decorations.push(Decoration.widget(head, () => createCursor(user), { key: clientId + '', side: 10 }))
        const from = math.min(anchor, head)
        const to = math.max(anchor, head)
        decorations.push(Decoration.inline(from, to, { style: `background-color: ${user.color}70` }, { inclusiveEnd: true, inclusiveStart: false }))
      }
    }
  })
  return DecorationSet.create(state.doc, decorations)
}

/**
 * A prosemirror plugin that listens to awareness information on Yjs.
 * This requires that a `prosemirrorPlugin` is also bound to the prosemirror.
 *
 * @public
 * @param {Awareness} awareness
 * @param {object} [opts]
 * @param {function(any):HTMLElement} [opts.cursorBuilder]
 * @param {function(any):any} [opts.getSelection]
 * @param {string} [opts.cursorStateField] By default all editor bindings use the awareness 'cursor' field to propagate cursor information.
 * @return {any}
 */
export const yCursorPlugin = (awareness, { cursorBuilder = defaultCursorBuilder, getSelection = state => state.selection } = {}, cursorStateField = 'cursor') => new Plugin({
  key: yCursorPluginKey,
  state: {
    init (_, state) {
      return createDecorations(state, awareness, cursorBuilder)
    },
    apply (tr, prevState, oldState, newState) {
      const ystate = ySyncPluginKey.getState(newState)
      const yCursorState = tr.getMeta(yCursorPluginKey)
      if ((ystate && ystate.isChangeOrigin) || (yCursorState && yCursorState.awarenessUpdated)) {
        return createDecorations(newState, awareness, cursorBuilder)
      }
      return prevState.map(tr.mapping, tr.doc)
    }
  },
  props: {
    decorations: state => {
      return yCursorPluginKey.getState(state)
    }
  },
  view: view => {
    const awarenessListener = () => {
      // @ts-ignore
      if (view.docView) {
        setMeta(view, yCursorPluginKey, { awarenessUpdated: true })
      }
    }
    const updateCursorInfo = () => {
      const ystate = ySyncPluginKey.getState(view.state)  ||  view.state['y-sync$'];
      // @note We make implicit checks when checking for the cursor property
      const current = awareness.getLocalState() || {}
      if (view.hasFocus() && ystate.binding !== null) {
        const selection = getSelection(view.state)
        /**
         * @type {Y.RelativePosition}
         */
        const anchor = absolutePositionToRelativePosition(selection.anchor, ystate.type, ystate.binding.mapping)
        /**
         * @type {Y.RelativePosition}
         */
        const head = absolutePositionToRelativePosition(selection.head, ystate.type, ystate.binding.mapping)
        if (current.cursor == null || !Y.compareRelativePositions(Y.createRelativePositionFromJSON(current.cursor.anchor), anchor) || !Y.compareRelativePositions(Y.createRelativePositionFromJSON(current.cursor.head), head)) {
          awareness.setLocalStateField(cursorStateField, {
            anchor, head
          })
        }
      } else if (current.cursor != null && relativePositionToAbsolutePosition(ystate.doc, ystate.type, Y.createRelativePositionFromJSON(current.cursor.anchor), ystate.binding.mapping) !== null) {
        // delete cursor information if current cursor information is owned by this editor binding
        awareness.setLocalStateField(cursorStateField, null)
      }
    }
    awareness.on('change', awarenessListener)
    view.dom.addEventListener('focusin', updateCursorInfo)
    view.dom.addEventListener('focusout', updateCursorInfo)
    return {
      update: updateCursorInfo,
      destroy: () => {
        view.dom.removeEventListener('focusin', updateCursorInfo)
        view.dom.removeEventListener('focusout', updateCursorInfo)
        awareness.off('change', awarenessListener)
        awareness.setLocalStateField(cursorStateField, null)
      }
    }
  }
})

ddd 与 ioc

当前项目涉及的实体以及对应关系并不是很多,但是已经存在重复代码及众多接口的前后端认知协同问题,需要重新考虑代码设计。一种可能的设计:
提炼出数据模型的 interface、入参 dto、出参 dto 以及模型操作的抽象接口作为 @think/model,前后端各自引入,各自实现对应的 service。其中,前端页面可使用 mobxmobx-react 对接接口实现服务,连接 react view 进行渲染。同时,借助 ioc 让代码整体只关注接口,而不关注具体的方法实现,完成解耦。

[FEATURE] oss/cos config

请描述功能点

配置里面是有oss/cos的配置可以填写,请问是怎样生效的,在默认的情况下,现在看到的是会存在本地上。
如果没有超级管理员 #63 ,这个功能改如何开启配置呢

一些想法

  1. 个人觉得超管,或者是系统管理这个身份还是蛮需要的,对于一些系统级别的配置
  2. think 的定位是什么?个人还是企业级。如果用户群体是企业的话,现在的一些设计是不能对齐企业组织架构的,也缺少了文件存储功能

[BUG]个人使用体验,问题反馈及建议汇总

之前一直在用Outline处理个人整理的一些资料,今天pull了最新的代码,认真体验了下think wiki,大概使用了80%的功能,发现了一些问题,也有不小的惊喜,感觉假以时日,赶超Outline肯定没问题!

发现的问题

  • 导入md文档部分内容会缺失
  • 思维导图(流程图)等图表编辑时,如果尚有选中的内容,切修改过内容文字,直接点击保存按钮,选中项的更改不会被保存。
  • 思维导图图,拖拽非root节点会导致画板迅速飞出可视区域。
  • 在其他网页,右键复制图片,粘贴在wiki中,只有第一次复制的图片会被成功粘贴,此后再重复复制其他图片,粘贴到wiki后,被上传并保存的始终都是头一张。
  • 在使用"-"生成列表之后,选中列表中部分文字,复制,粘贴,会自动增加“* ”在粘贴的文字前
  • 粘贴图片后,有几率导致“正在上传image中”区域不会自动消失

使用建议

  • 从网页中粘贴的格式和样式排版无法识别
  • 移动端使用体验还可以再优化下
  • 希望可以增加Chrome的Web应用,就可以将Web应用添加到内容中
  • 导入文档功能,建议增加拖拽区域,方便快速提交文件
  • 建议在文档的侧边栏直接支持拖拽排序,目前文档管理操作起来不是很直观,编辑排序后会容易忘记点击提交按钮
  • 建议增加星标功能,类似于Outline的文档星标

pnpm run build failed. error TS6053: File 'src/*.ts' not found.

GOAL: 本地源代码运行(生产环境)
After pnpm install done

$ pnpm run build

> think@ build D:\project\think\think
> pnpm build:dep && pnpm build:server && pnpm build:client


> think@ build:dep D:\project\think\think
> pnpm build:constants && pnpm build:domains && pnpm build:config


> think@ build:constants D:\project\think\think
> pnpm run --dir packages/constants build


> @think/[email protected] build D:\project\think\think\packages\constants
> tsc src/*.ts --outDir lib --skipLibCheck --declaration --lib ES6

error TS6053: File 'src/*.ts' not found.
  The file is in the program because:
    Root file specified for compilation
 ELIFECYCLE  Command failed with exit code 2.
 ELIFECYCLE  Command failed with exit code 1.
 ELIFECYCLE  Command failed with exit code 1.
 ELIFECYCLE  Command failed with exit code 1.

try pnpm run dev successfully but pnpm run build failed, how can i build it

docker-compose部署教程

已提交PR #53 ,有需要的可自行拉取最新代码执行.

项目作者已合并,请前往项目主页查看教程.

模板创建bug

路径:模板->我创建的->新建模板,会出现如下图所示问题
image
返回后,显示创建成功
image

插入的表格不显示

1、在插入表格的时候,表格一直不显示,貌似无法正确的插入表格;
2、在两段文本中间,插入代码块时,插入点后面的文本会被放入代码块中;

很不错的一个知识库啊,非常的需要这种开源又好用的知识库,和语雀很像,支持大佬继续开发优化;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.