Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
5d677c3c
Commit
5d677c3c
authored
Dec 17, 2023
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: implement node renderer components
parent
28c05497
Changes
30
Hide whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
456 additions
and
26 deletions
+456
-26
markdown_service.proto
proto/api/v2/markdown_service.proto
+0
-1
README.md
proto/gen/api/v2/README.md
+1
-1
markdown_service.pb.go
proto/gen/api/v2/markdown_service.pb.go
+0
-1
Blockquote.tsx
web/src/components/MemoContentV1/Blockquote.tsx
+18
-0
Bold.tsx
web/src/components/MemoContentV1/Bold.tsx
+19
-0
BoldItalic.tsx
web/src/components/MemoContentV1/BoldItalic.tsx
+14
-0
Code.tsx
web/src/components/MemoContentV1/Code.tsx
+9
-0
CodeBlock.tsx
web/src/components/MemoContentV1/CodeBlock.tsx
+42
-0
EscapingCharacter.tsx
web/src/components/MemoContentV1/EscapingCharacter.tsx
+9
-0
Heading.tsx
web/src/components/MemoContentV1/Heading.tsx
+20
-0
HorizontalRule.tsx
web/src/components/MemoContentV1/HorizontalRule.tsx
+9
-0
Image.tsx
web/src/components/MemoContentV1/Image.tsx
+10
-0
Italic.tsx
web/src/components/MemoContentV1/Italic.tsx
+10
-0
LineBreak.tsx
web/src/components/MemoContentV1/LineBreak.tsx
+7
-0
Link.tsx
web/src/components/MemoContentV1/Link.tsx
+14
-0
OrderedList.tsx
web/src/components/MemoContentV1/OrderedList.tsx
+26
-0
Paragraph.tsx
web/src/components/MemoContentV1/Paragraph.tsx
+18
-0
Renderer.tsx
web/src/components/MemoContentV1/Renderer.tsx
+92
-0
Strikethrough.tsx
web/src/components/MemoContentV1/Strikethrough.tsx
+9
-0
Tag.tsx
web/src/components/MemoContentV1/Tag.tsx
+9
-0
TaskList.tsx
web/src/components/MemoContentV1/TaskList.tsx
+28
-0
Text.tsx
web/src/components/MemoContentV1/Text.tsx
+9
-0
UnorderedList.tsx
web/src/components/MemoContentV1/UnorderedList.tsx
+26
-0
index.tsx
web/src/components/MemoContentV1/index.tsx
+48
-0
context.ts
web/src/components/MemoContentV1/types/context.ts
+1
-0
index.ts
web/src/components/MemoContentV1/types/index.ts
+1
-0
index.tsx
web/src/components/MemoEditor/index.tsx
+2
-11
grpcweb.ts
web/src/grpcweb.ts
+3
-0
memo-content.less
web/src/less/memo-content.less
+0
-10
MemoDetail.tsx
web/src/pages/MemoDetail.tsx
+2
-2
No files found.
proto/api/v2/markdown_service.proto
View file @
5d677c3c
...
...
@@ -46,7 +46,6 @@ enum NodeType {
ESCAPING_CHARACTER
=
19
;
}
// Define the Node message.
message
Node
{
NodeType
type
=
1
;
oneof
node
{
...
...
proto/gen/api/v2/README.md
View file @
5d677c3c
...
...
@@ -1092,7 +1092,7 @@
<a
name=
"memos-api-v2-Node"
></a>
### Node
Define the Node message.
| Field | Type | Label | Description |
...
...
proto/gen/api/v2/markdown_service.pb.go
View file @
5d677c3c
...
...
@@ -215,7 +215,6 @@ func (x *ParseMarkdownResponse) GetNodes() []*Node {
return
nil
}
// Define the Node message.
type
Node
struct
{
state
protoimpl
.
MessageState
sizeCache
protoimpl
.
SizeCache
...
...
web/src/components/MemoContentV1/Blockquote.tsx
0 → 100644
View file @
5d677c3c
import
{
Node
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Renderer
from
"./Renderer"
;
interface
Props
{
children
:
Node
[];
}
const
Blockquote
:
React
.
FC
<
Props
>
=
({
children
}:
Props
)
=>
{
return
(
<
blockquote
>
{
children
.
map
((
child
,
index
)
=>
(
<
Renderer
key=
{
`${child.type}-${index}`
}
node=
{
child
}
/>
))
}
</
blockquote
>
);
};
export
default
Blockquote
;
web/src/components/MemoContentV1/Bold.tsx
0 → 100644
View file @
5d677c3c
import
{
Node
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Renderer
from
"./Renderer"
;
interface
Props
{
symbol
:
string
;
children
:
Node
[];
}
const
Bold
:
React
.
FC
<
Props
>
=
({
children
}:
Props
)
=>
{
return
(
<
strong
>
{
children
.
map
((
child
,
index
)
=>
(
<
Renderer
key=
{
`${child.type}-${index}`
}
node=
{
child
}
/>
))
}
</
strong
>
);
};
export
default
Bold
;
web/src/components/MemoContentV1/BoldItalic.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
symbol
:
string
;
content
:
string
;
}
const
BoldItalic
:
React
.
FC
<
Props
>
=
({
content
}:
Props
)
=>
{
return
(
<
strong
>
<
em
>
{
content
}
</
em
>
</
strong
>
);
};
export
default
BoldItalic
;
web/src/components/MemoContentV1/Code.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
content
:
string
;
}
const
Code
:
React
.
FC
<
Props
>
=
({
content
}:
Props
)
=>
{
return
<
code
>
{
content
}
</
code
>;
};
export
default
Code
;
web/src/components/MemoContentV1/CodeBlock.tsx
0 → 100644
View file @
5d677c3c
import
classNames
from
"classnames"
;
import
copy
from
"copy-to-clipboard"
;
import
hljs
from
"highlight.js"
;
import
toast
from
"react-hot-toast"
;
interface
Props
{
language
:
string
;
content
:
string
;
}
const
CodeBlock
:
React
.
FC
<
Props
>
=
({
language
,
content
}:
Props
)
=>
{
const
formatedLanguage
=
language
.
toLowerCase
()
||
"plaintext"
;
let
highlightedCode
=
hljs
.
highlightAuto
(
content
).
value
;
try
{
const
temp
=
hljs
.
highlight
(
content
,
{
language
:
formatedLanguage
,
}).
value
;
highlightedCode
=
temp
;
}
catch
(
error
)
{
// Skip error and use default highlighted code.
}
const
handleCopyButtonClick
=
()
=>
{
copy
(
content
);
toast
.
success
(
"Copied to clipboard!"
);
};
return
(
<
pre
className=
"group w-full my-1 p-3 rounded bg-gray-100 dark:bg-zinc-600 whitespace-pre-wrap relative"
>
<
button
className=
"text-xs font-mono italic absolute top-0 right-0 px-2 leading-6 border btn-text rounded opacity-0 group-hover:opacity-60"
onClick=
{
handleCopyButtonClick
}
>
copy
</
button
>
<
code
className=
{
classNames
(
`language-${formatedLanguage}`
,
"block"
)
}
dangerouslySetInnerHTML=
{
{
__html
:
highlightedCode
}
}
></
code
>
</
pre
>
);
};
export
default
CodeBlock
;
web/src/components/MemoContentV1/EscapingCharacter.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
symbol
:
string
;
}
const
EscapingCharacter
:
React
.
FC
<
Props
>
=
({
symbol
}:
Props
)
=>
{
return
<
span
>
{
symbol
}
</
span
>;
};
export
default
EscapingCharacter
;
web/src/components/MemoContentV1/Heading.tsx
0 → 100644
View file @
5d677c3c
import
{
Node
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Renderer
from
"./Renderer"
;
interface
Props
{
level
:
number
;
children
:
Node
[];
}
const
Heading
:
React
.
FC
<
Props
>
=
({
level
,
children
}:
Props
)
=>
{
const
Head
=
`h
${
level
}
`
as
keyof
JSX
.
IntrinsicElements
;
return
(
<
Head
>
{
children
.
map
((
child
,
index
)
=>
(
<
Renderer
key=
{
`${child.type}-${index}`
}
node=
{
child
}
/>
))
}
</
Head
>
);
};
export
default
Heading
;
web/src/components/MemoContentV1/HorizontalRule.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
symbol
:
string
;
}
const
HorizontalRule
:
React
.
FC
<
Props
>
=
()
=>
{
return
<
hr
/>;
};
export
default
HorizontalRule
;
web/src/components/MemoContentV1/Image.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
altText
:
string
;
url
:
string
;
}
const
Image
:
React
.
FC
<
Props
>
=
({
altText
,
url
}:
Props
)
=>
{
return
<
img
alt=
{
altText
}
src=
{
url
}
/>;
};
export
default
Image
;
web/src/components/MemoContentV1/Italic.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
symbol
:
string
;
content
:
string
;
}
const
Italic
:
React
.
FC
<
Props
>
=
({
content
}:
Props
)
=>
{
return
<
em
>
{
content
}
</
em
>;
};
export
default
Italic
;
web/src/components/MemoContentV1/LineBreak.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{}
const
LineBreak
:
React
.
FC
<
Props
>
=
()
=>
{
return
<
br
/>;
};
export
default
LineBreak
;
web/src/components/MemoContentV1/Link.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
text
:
string
;
url
:
string
;
}
const
Link
:
React
.
FC
<
Props
>
=
({
text
,
url
}:
Props
)
=>
{
return
(
<
a
className=
"text-blue-600 dark:text-blue-400 cursor-pointer underline break-all hover:opacity-80 decoration-1"
href=
{
url
}
>
{
text
}
</
a
>
);
};
export
default
Link
;
web/src/components/MemoContentV1/OrderedList.tsx
0 → 100644
View file @
5d677c3c
import
{
Node
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Renderer
from
"./Renderer"
;
interface
Props
{
number
:
string
;
children
:
Node
[];
}
const
OrderedList
:
React
.
FC
<
Props
>
=
({
number
,
children
}:
Props
)
=>
{
return
(
<
ol
>
<
li
className=
"grid grid-cols-[24px_1fr] gap-1"
>
<
div
className=
"w-7 h-6 flex justify-center items-center"
>
<
span
className=
"opacity-80"
>
{
number
}
.
</
span
>
</
div
>
<
div
>
{
children
.
map
((
child
,
index
)
=>
(
<
Renderer
key=
{
`${child.type}-${index}`
}
node=
{
child
}
/>
))
}
</
div
>
</
li
>
</
ol
>
);
};
export
default
OrderedList
;
web/src/components/MemoContentV1/Paragraph.tsx
0 → 100644
View file @
5d677c3c
import
{
Node
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Renderer
from
"./Renderer"
;
interface
Props
{
children
:
Node
[];
}
const
Paragraph
:
React
.
FC
<
Props
>
=
({
children
}:
Props
)
=>
{
return
(
<
p
>
{
children
.
map
((
child
,
index
)
=>
(
<
Renderer
key=
{
`${child.type}-${index}`
}
node=
{
child
}
/>
))
}
</
p
>
);
};
export
default
Paragraph
;
web/src/components/MemoContentV1/Renderer.tsx
0 → 100644
View file @
5d677c3c
import
{
BlockquoteNode
,
BoldItalicNode
,
BoldNode
,
CodeBlockNode
,
CodeNode
,
EscapingCharacterNode
,
HeadingNode
,
HorizontalRuleNode
,
ImageNode
,
ItalicNode
,
LinkNode
,
Node
,
NodeType
,
OrderedListNode
,
ParagraphNode
,
StrikethroughNode
,
TagNode
,
TaskListNode
,
TextNode
,
UnorderedListNode
,
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Blockquote
from
"./Blockquote"
;
import
Bold
from
"./Bold"
;
import
BoldItalic
from
"./BoldItalic"
;
import
Code
from
"./Code"
;
import
CodeBlock
from
"./CodeBlock"
;
import
EscapingCharacter
from
"./EscapingCharacter"
;
import
Heading
from
"./Heading"
;
import
HorizontalRule
from
"./HorizontalRule"
;
import
Image
from
"./Image"
;
import
Italic
from
"./Italic"
;
import
LineBreak
from
"./LineBreak"
;
import
Link
from
"./Link"
;
import
OrderedList
from
"./OrderedList"
;
import
Paragraph
from
"./Paragraph"
;
import
Strikethrough
from
"./Strikethrough"
;
import
Tag
from
"./Tag"
;
import
TaskList
from
"./TaskList"
;
import
Text
from
"./Text"
;
import
UnorderedList
from
"./UnorderedList"
;
interface
Props
{
node
:
Node
;
}
const
Renderer
:
React
.
FC
<
Props
>
=
({
node
}:
Props
)
=>
{
switch
(
node
.
type
)
{
case
NodeType
.
LINE_BREAK
:
return
<
LineBreak
/>;
case
NodeType
.
PARAGRAPH
:
return
<
Paragraph
{
...
(
node
.
paragraphNode
as
ParagraphNode
)}
/>;
case
NodeType
.
CODE_BLOCK
:
return
<
CodeBlock
{
...
(
node
.
codeBlockNode
as
CodeBlockNode
)}
/>;
case
NodeType
.
HEADING
:
return
<
Heading
{
...
(
node
.
headingNode
as
HeadingNode
)}
/>;
case
NodeType
.
HORIZONTAL_RULE
:
return
<
HorizontalRule
{
...
(
node
.
horizontalRuleNode
as
HorizontalRuleNode
)}
/>;
case
NodeType
.
BLOCKQUOTE
:
return
<
Blockquote
{
...
(
node
.
blockquoteNode
as
BlockquoteNode
)}
/>;
case
NodeType
.
ORDERED_LIST
:
return
<
OrderedList
{
...
(
node
.
orderedListNode
as
OrderedListNode
)}
/>;
case
NodeType
.
UNORDERED_LIST
:
return
<
UnorderedList
{
...
(
node
.
unorderedListNode
as
UnorderedListNode
)}
/>;
case
NodeType
.
TASK_LIST
:
return
<
TaskList
{
...
(
node
.
taskListNode
as
TaskListNode
)}
/>;
case
NodeType
.
TEXT
:
return
<
Text
{
...
(
node
.
textNode
as
TextNode
)}
/>;
case
NodeType
.
BOLD
:
return
<
Bold
{
...
(
node
.
boldNode
as
BoldNode
)}
/>;
case
NodeType
.
ITALIC
:
return
<
Italic
{
...
(
node
.
italicNode
as
ItalicNode
)}
/>;
case
NodeType
.
BOLD_ITALIC
:
return
<
BoldItalic
{
...
(
node
.
boldItalicNode
as
BoldItalicNode
)}
/>;
case
NodeType
.
CODE
:
return
<
Code
{
...
(
node
.
codeNode
as
CodeNode
)}
/>;
case
NodeType
.
IMAGE
:
return
<
Image
{
...
(
node
.
imageNode
as
ImageNode
)}
/>;
case
NodeType
.
LINK
:
return
<
Link
{
...
(
node
.
linkNode
as
LinkNode
)}
/>;
case
NodeType
.
TAG
:
return
<
Tag
{
...
(
node
.
tagNode
as
TagNode
)}
/>;
case
NodeType
.
STRIKETHROUGH
:
return
<
Strikethrough
{
...
(
node
.
strikethroughNode
as
StrikethroughNode
)}
/>;
case
NodeType
.
ESCAPING_CHARACTER
:
return
<
EscapingCharacter
{
...
(
node
.
escapingCharacterNode
as
EscapingCharacterNode
)}
/>;
default
:
return
null
;
}
};
export
default
Renderer
;
web/src/components/MemoContentV1/Strikethrough.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
content
:
string
;
}
const
Strikethrough
:
React
.
FC
<
Props
>
=
({
content
}:
Props
)
=>
{
return
<
del
>
{
content
}
</
del
>;
};
export
default
Strikethrough
;
web/src/components/MemoContentV1/Tag.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
content
:
string
;
}
const
Tag
:
React
.
FC
<
Props
>
=
({
content
}:
Props
)
=>
{
return
<
span
className=
"inline-block w-auto text-blue-600 dark:text-blue-400"
>
#
{
content
}
</
span
>;
};
export
default
Tag
;
web/src/components/MemoContentV1/TaskList.tsx
0 → 100644
View file @
5d677c3c
import
{
Checkbox
}
from
"@mui/joy"
;
import
{
Node
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Renderer
from
"./Renderer"
;
interface
Props
{
symbol
:
string
;
complete
:
boolean
;
children
:
Node
[];
}
const
TaskList
:
React
.
FC
<
Props
>
=
({
complete
,
children
}:
Props
)
=>
{
return
(
<
ul
>
<
li
className=
"grid grid-cols-[24px_1fr] gap-1"
>
<
div
className=
"w-7 h-6 flex justify-center items-center"
>
<
Checkbox
size=
"sm"
checked=
{
complete
}
readOnly
/>
</
div
>
<
div
>
{
children
.
map
((
child
,
index
)
=>
(
<
Renderer
key=
{
`${child.type}-${index}`
}
node=
{
child
}
/>
))
}
</
div
>
</
li
>
</
ul
>
);
};
export
default
TaskList
;
web/src/components/MemoContentV1/Text.tsx
0 → 100644
View file @
5d677c3c
interface
Props
{
content
:
string
;
}
const
Text
:
React
.
FC
<
Props
>
=
({
content
}:
Props
)
=>
{
return
<
span
>
{
content
}
</
span
>;
};
export
default
Text
;
web/src/components/MemoContentV1/UnorderedList.tsx
0 → 100644
View file @
5d677c3c
import
{
Node
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Renderer
from
"./Renderer"
;
interface
Props
{
symbol
:
string
;
children
:
Node
[];
}
const
UnorderedList
:
React
.
FC
<
Props
>
=
({
children
}:
Props
)
=>
{
return
(
<
ul
>
<
li
className=
"grid grid-cols-[24px_1fr] gap-1"
>
<
div
className=
"w-7 h-6 flex justify-center items-center"
>
<
span
className=
"opacity-80"
>
•
</
span
>
</
div
>
<
div
>
{
children
.
map
((
child
,
index
)
=>
(
<
Renderer
key=
{
`${child.type}-${index}`
}
node=
{
child
}
/>
))
}
</
div
>
</
li
>
</
ul
>
);
};
export
default
UnorderedList
;
web/src/components/MemoContentV1/index.tsx
0 → 100644
View file @
5d677c3c
import
{
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
markdownServiceClient
}
from
"@/grpcweb"
;
import
{
Node
}
from
"@/types/proto/api/v2/markdown_service"
;
import
Renderer
from
"./Renderer"
;
interface
Props
{
content
:
string
;
className
?:
string
;
onMemoContentClick
?:
(
e
:
React
.
MouseEvent
)
=>
void
;
}
const
MemoContentV1
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
className
,
content
,
onMemoContentClick
}
=
props
;
const
[
nodes
,
setNodes
]
=
useState
<
Node
[]
>
([]);
const
memoContentContainerRef
=
useRef
<
HTMLDivElement
>
(
null
);
useEffect
(()
=>
{
markdownServiceClient
.
parseMarkdown
({
markdown
:
content
,
})
.
then
(({
nodes
})
=>
{
setNodes
(
nodes
);
});
},
[
content
]);
const
handleMemoContentClick
=
async
(
e
:
React
.
MouseEvent
)
=>
{
if
(
onMemoContentClick
)
{
onMemoContentClick
(
e
);
}
};
return
(
<
div
className=
{
`w-full flex flex-col justify-start items-start text-gray-800 dark:text-gray-300 ${className || ""}`
}
>
<
div
ref=
{
memoContentContainerRef
}
className=
"w-full max-w-full word-break text-base leading-6 space-y-1"
onClick=
{
handleMemoContentClick
}
>
{
nodes
.
map
((
node
,
index
)
=>
(
<
Renderer
key=
{
`${node.type}-${index}`
}
node=
{
node
}
/>
))
}
</
div
>
</
div
>
);
};
export
default
MemoContentV1
;
web/src/components/MemoContentV1/types/context.ts
0 → 100644
View file @
5d677c3c
export
interface
RendererContext
{}
web/src/components/MemoContentV1/types/index.ts
0 → 100644
View file @
5d677c3c
export
*
from
"./context"
;
web/src/components/MemoEditor/index.tsx
View file @
5d677c3c
import
{
Select
,
Option
,
Button
,
IconButton
,
Divider
}
from
"@mui/joy"
;
import
{
isNumber
,
last
,
uniq
,
uniq
By
}
from
"lodash-es"
;
import
{
isNumber
,
last
,
uniqBy
}
from
"lodash-es"
;
import
React
,
{
useCallback
,
useEffect
,
useMemo
,
useRef
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
useLocalStorage
from
"react-use/lib/useLocalStorage"
;
import
{
TAB_SPACE_WIDTH
,
UNKNOWN_ID
,
VISIBILITY_SELECTOR_ITEMS
}
from
"@/helpers/consts"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
getMatchedNodes
}
from
"@/labs/marked"
;
import
{
useGlobalStore
,
useMemoStore
,
useResourceStore
,
useTagStore
}
from
"@/store/module"
;
import
{
useGlobalStore
,
useMemoStore
,
useResourceStore
}
from
"@/store/module"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
Resource
}
from
"@/types/proto/api/v2/resource_service"
;
import
{
UserSetting
,
User_Role
}
from
"@/types/proto/api/v2/user_service"
;
...
...
@@ -52,7 +51,6 @@ const MemoEditor = (props: Props) => {
}
=
useGlobalStore
();
const
userV1Store
=
useUserV1Store
();
const
memoStore
=
useMemoStore
();
const
tagStore
=
useTagStore
();
const
resourceStore
=
useResourceStore
();
const
currentUser
=
useCurrentUser
();
const
[
state
,
setState
]
=
useState
<
State
>
({
...
...
@@ -335,13 +333,6 @@ const MemoEditor = (props: Props) => {
};
});
// Upsert tag with the content.
const
matchedNodes
=
getMatchedNodes
(
content
);
const
tagNameList
=
uniq
(
matchedNodes
.
filter
((
node
)
=>
node
.
parserName
===
"tag"
).
map
((
node
)
=>
node
.
matchedContent
.
slice
(
1
)));
for
(
const
tagName
of
tagNameList
)
{
await
tagStore
.
upsertTag
(
tagName
);
}
setState
((
prevState
)
=>
({
...
prevState
,
resourceList
:
[],
...
...
web/src/grpcweb.ts
View file @
5d677c3c
...
...
@@ -2,6 +2,7 @@ import { createChannel, createClientFactory, FetchTransport } from "nice-grpc-we
import
{
ActivityServiceDefinition
}
from
"./types/proto/api/v2/activity_service"
;
import
{
AuthServiceDefinition
}
from
"./types/proto/api/v2/auth_service"
;
import
{
InboxServiceDefinition
}
from
"./types/proto/api/v2/inbox_service"
;
import
{
MarkdownServiceDefinition
}
from
"./types/proto/api/v2/markdown_service"
;
import
{
MemoServiceDefinition
}
from
"./types/proto/api/v2/memo_service"
;
import
{
ResourceServiceDefinition
}
from
"./types/proto/api/v2/resource_service"
;
import
{
SystemServiceDefinition
}
from
"./types/proto/api/v2/system_service"
;
...
...
@@ -35,3 +36,5 @@ export const inboxServiceClient = clientFactory.create(InboxServiceDefinition, c
export
const
activityServiceClient
=
clientFactory
.
create
(
ActivityServiceDefinition
,
channel
);
export
const
webhookServiceClient
=
clientFactory
.
create
(
WebhookServiceDefinition
,
channel
);
export
const
markdownServiceClient
=
clientFactory
.
create
(
MarkdownServiceDefinition
,
channel
);
web/src/less/memo-content.less
View file @
5d677c3c
...
...
@@ -69,16 +69,6 @@
code {
@apply block;
}
&:hover {
.codeblock-copy-btn {
@apply flex;
}
}
.codeblock-copy-btn {
@apply btn-normal absolute hidden top-2 right-2 border-solid border-2;
}
}
code {
...
...
web/src/pages/MemoDetail.tsx
View file @
5d677c3c
...
...
@@ -6,7 +6,7 @@ import { Link, useParams } from "react-router-dom";
import
FloatingNavButton
from
"@/components/FloatingNavButton"
;
import
Icon
from
"@/components/Icon"
;
import
Memo
from
"@/components/Memo"
;
import
MemoContent
from
"@/components/MemoContent
"
;
import
MemoContent
V1
from
"@/components/MemoContentV1
"
;
import
MemoEditor
from
"@/components/MemoEditor"
;
import
showMemoEditorDialog
from
"@/components/MemoEditor/MemoEditorDialog"
;
import
MemoRelationListView
from
"@/components/MemoRelationListView"
;
...
...
@@ -133,7 +133,7 @@ const MemoDetail = () => {
<
div
className=
"w-full mb-4 flex flex-row justify-start items-center mr-1"
>
<
span
className=
"text-gray-400 select-none"
>
{
getDateTimeString
(
memo
.
displayTs
)
}
</
span
>
</
div
>
<
MemoContent
content=
{
memo
.
content
}
/>
<
MemoContent
V1
content=
{
memo
.
content
}
/>
<
MemoResourceListView
resourceList=
{
memo
.
resourceList
}
/>
<
MemoRelationListView
memo=
{
memo
}
relationList=
{
referenceRelations
}
/>
<
div
className=
"w-full mt-4 flex flex-col sm:flex-row justify-start sm:justify-between sm:items-center gap-2"
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment