diff --git a/README.md b/README.md index 443656b17..6d34cd11d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,12 @@ Then you can easily regenerate proto files: make protos ``` -### Run tests +#### Run tests +Generate mocks: +``` +make test-deps +``` + GO test: ``` make test diff --git a/anymark/_test/testData.json b/anymark/_test/testData.json index ffc4708f6..5855ce721 100644 --- a/anymark/_test/testData.json +++ b/anymark/_test/testData.json @@ -1,4 +1,17 @@ [ + + { + "desc": "wbr 1", + "html": "www.merkle.com", + "blocks": [{"id":"1","Content":{"text":{"text":"www.merkle.com", "marks":{"marks":[{"range":{"to":14},"type":5,"param":"https://www.merkle.com/"}]}}}}] + }, + + { + "desc": "th td 1", + "html": "
Ralph Merkle
\"Ralph
Merkle at the Singularity Summit 2007
BornFebruary 2, 1952 (age 68)
Berkeley, California
NationalityAmerican
CitizenshipAmerican
Alma mater
Known for
Spouse(s)Carol Shaw
AwardsIEEE Richard W. Hamming Medal (2010)
Computer History Museum Fellow (2011)[2]
Scientific career
FieldsPublic key cryptography, cryonics
Institutions
ThesisSecrecy, authentication and public key systems
Doctoral advisorMartin Hellman
Websitewww.merkle.com
", + "blocks": [{"id":"1","Content":{"text":{"text":"Ralph Merkle","marks":{"marks":[{"range":{"to":12},"type":3}]}}}},{"id":"2","Content":{"file":{"name":"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Ralph_Merkle.png/220px-Ralph_Merkle.png","type":2}}},{"id":"3","Content":{"text":{"text":"Merkle at the Singularity Summit 2007","marks":{"marks":[{"range":{"from":14,"to":32},"type":5,"param":"https://en.wikipedia.org/wiki/Singularity_Summit"}]}}}},{"id":"4","Content":{"text":{"text":"Born","marks":{"marks":[{"range":{"to":4},"type":3}]}}}},{"id":"5","Content":{"text":{"text":"February 2, 1952 (age 68)","marks":{}}}},{"id":"6","Content":{"text":{"text":"Berkeley, California","marks":{}}}},{"id":"7","Content":{"text":{"text":"Nationality","marks":{"marks":[{"range":{"to":11},"type":3}]}}}},{"id":"8","Content":{"text":{"text":"American","marks":{}}}},{"id":"9","Content":{"text":{"text":"Citizenship","marks":{"marks":[{"range":{"to":11},"type":3}]}}}},{"id":"10","Content":{"text":{"text":"American","marks":{"marks":[{"range":{"to":8},"type":5,"param":"https://en.wikipedia.org/wiki/United_States"}]}}}},{"id":"11","Content":{"text":{"text":"Alma mater","marks":{"marks":[{"range":{"to":10},"type":3}]}}}},{"id":"12","Content":{"text":{"text":"UC Berkeley (B.A., 1974; M.S., 1977)","style":9,"marks":{"marks":[{"range":{"to":11},"type":5,"param":"https://en.wikipedia.org/wiki/University_of_California,_Berkeley"}]}}}},{"id":"13","Content":{"text":{"text":"Stanford University (Ph.D., 1979)","style":9,"marks":{"marks":[{"range":{"to":19},"type":5,"param":"https://en.wikipedia.org/wiki/Stanford_University"}]}}}},{"id":"14","Content":{"text":{"text":"Known for","marks":{"marks":[{"range":{"to":9},"type":3}]}}}},{"id":"15","Content":{"text":{"text":"Co-inventor of public key cryptography","style":9,"marks":{"marks":[{"range":{"from":15,"to":38},"type":5,"param":"https://en.wikipedia.org/wiki/Public_key_cryptography"}]}}}},{"id":"16","Content":{"text":{"text":"Merkle tree[1]","style":9,"marks":{"marks":[{"range":{"to":11},"type":5,"param":"https://en.wikipedia.org/wiki/Merkle_tree"},{"range":{"from":11,"to":14},"type":5,"param":"https://en.wikipedia.org/wiki/Ralph_Merkle#cite_note-1"}]}}}},{"id":"17","Content":{"text":{"text":"Merkle's puzzles","style":9,"marks":{"marks":[{"range":{"to":16},"type":5,"param":"https://en.wikipedia.org/wiki/Merkle's_puzzles"}]}}}},{"id":"18","Content":{"text":{"text":"Merkle–Hellman knapsack cryptosystem","style":9,"marks":{"marks":[{"range":{"to":36},"type":5,"param":"https://en.wikipedia.org/wiki/Merkle–Hellman_knapsack_cryptosystem"}]}}}},{"id":"19","Content":{"text":{"text":"Merkle–Damgård construction","style":9,"marks":{"marks":[{"range":{"to":27},"type":5,"param":"https://en.wikipedia.org/wiki/Merkle–Damgård_construction"}]}}}},{"id":"20","Content":{"text":{"text":"Spouse(s)","marks":{"marks":[{"range":{"to":9},"type":3}]}}}},{"id":"21","Content":{"text":{"text":"Carol Shaw","marks":{"marks":[{"range":{"to":10},"type":5,"param":"https://en.wikipedia.org/wiki/Carol_Shaw_(video_game_designer)"}]}}}},{"id":"22","Content":{"text":{"text":"Awards","marks":{"marks":[{"range":{"to":6},"type":3}]}}}},{"id":"23","Content":{"text":{"text":"IEEE Richard W. Hamming Medal (2010)\nComputer History Museum Fellow (2011)[2]","marks":{"marks":[{"range":{"to":29},"type":5,"param":"https://en.wikipedia.org/wiki/IEEE_Richard_W._Hamming_Medal"},{"range":{"from":37,"to":60},"type":5,"param":"https://en.wikipedia.org/wiki/Computer_History_Museum"},{"range":{"from":74,"to":77},"type":5,"param":"https://en.wikipedia.org/wiki/Ralph_Merkle#cite_note-2"}]}}}},{"id":"24","Content":{"text":{"text":"Scientific career","marks":{"marks":[{"range":{"to":17},"type":3}]}}}},{"id":"25","Content":{"text":{"text":"Fields","marks":{"marks":[{"range":{"to":6},"type":3}]}}}},{"id":"26","Content":{"text":{"text":"Public key cryptography, cryonics","marks":{"marks":[{"range":{"to":23},"type":5,"param":"https://en.wikipedia.org/wiki/Public_key_cryptography"},{"range":{"from":25,"to":33},"type":5,"param":"https://en.wikipedia.org/wiki/Cryonics"}]}}}},{"id":"27","Content":{"text":{"text":"Institutions","marks":{"marks":[{"range":{"to":12},"type":3}]}}}},{"id":"28","Content":{"text":{"text":"Singularity University","style":9,"marks":{"marks":[{"range":{"to":22},"type":5,"param":"https://en.wikipedia.org/wiki/Singularity_University"}]}}}},{"id":"29","Content":{"text":{"text":"Alcor Life Extension Foundation","style":9,"marks":{"marks":[{"range":{"to":31},"type":5,"param":"https://en.wikipedia.org/wiki/Alcor_Life_Extension_Foundation"}]}}}},{"id":"30","Content":{"text":{"text":"Institute for Molecular Manufacturing","style":9,"marks":{}}}},{"id":"31","Content":{"text":{"text":"Elxsi","style":9,"marks":{}}}},{"id":"32","Content":{"text":{"text":"Georgia Institute of Technology","style":9,"marks":{"marks":[{"range":{"to":31},"type":5,"param":"https://en.wikipedia.org/wiki/Georgia_Institute_of_Technology"}]}}}},{"id":"33","Content":{"text":{"text":"Thesis","marks":{"marks":[{"range":{"to":6},"type":5,"param":"https://en.wikipedia.org/wiki/Thesis"},{"range":{"to":6},"type":3}]}}}},{"id":"34","Content":{"text":{"text":"Secrecy, authentication and public key systems","marks":{"marks":[{"range":{"to":46},"type":2},{"range":{"to":46},"type":5,"param":"https://www.merkle.com/papers/Thesis1979.pdf"}]}}}},{"id":"35","Content":{"text":{"text":"Doctoral advisor","marks":{"marks":[{"range":{"to":16},"type":5,"param":"https://en.wikipedia.org/wiki/Doctoral_advisor"},{"range":{"to":16},"type":3}]}}}},{"id":"36","Content":{"text":{"text":"Martin Hellman","marks":{"marks":[{"range":{"to":14},"type":5,"param":"https://en.wikipedia.org/wiki/Martin_Hellman"}]}}}},{"id":"37","Content":{"text":{"text":"Website","marks":{"marks":[{"range":{"to":7},"type":3}]}}}},{"id":"38","Content":{"text":{"text":"www.merkle.com","marks":{"marks":[{"range":{"to":14},"type":5,"param":"https://www.merkle.com/"}]}}}}] + }, + { "desc": "links 1", "html": "

War is a state of organized armed conflict between states or non-state actors. War is characterized by the use of lethal violence against others—whether between combatants or upon non-combatants—to achieve military goals through force. Lesser, often spontaneous conflicts, such as brawls, riots, revolts, and melees, are not considered to be warfare. Revolutions can be nonviolent or an organized and armed revolution which denotes a state of war. During the 20th century, it is estimated that between 167 and 188 million people died as a result of war.[258] A common definition defines war as a series of military campaigns between at least two opposing sides involving a dispute over sovereignty, territory, resources, religion, or other issues. A war between internal elements of a state is a civil war.\n

", diff --git a/anymark/markdown.go b/anymark/markdown.go index b82823ae8..30dc67c1a 100644 --- a/anymark/markdown.go +++ b/anymark/markdown.go @@ -42,6 +42,8 @@ var ( linkLeftEdge = regexp.MustCompile(`(\S)\[`) reEmptyLinkText = regexp.MustCompile(`\[[\s]*?\]\(([\s\S]*?)\)`) reWikiCode = regexp.MustCompile(`([\s\S]*?)`) + + reWikiWbr = regexp.MustCompile(`]*>`) ) // Convert interprets a UTF-8 @@ -169,6 +171,7 @@ func (m *markdown) HTMLToBlocks(source []byte) (err error, blocks []*model.Block // special wiki spaces preprocessedSource = strings.ReplaceAll(preprocessedSource, " ", " ") + preprocessedSource = reWikiWbr.ReplaceAllString(preprocessedSource, ``) // Pattern:
 \n console \n \n . \n \n log \n
 	preprocessedSource = reWikiCode.ReplaceAllString(preprocessedSource, `$1`)
diff --git a/core/block/editor/archive.go b/core/block/editor/archive.go
index 963f1bdc6..6b0cd4a2c 100644
--- a/core/block/editor/archive.go
+++ b/core/block/editor/archive.go
@@ -28,8 +28,8 @@ type Archive struct {
 	smartblock.SmartBlock
 }
 
-func (p *Archive) Init(s source.Source) (err error) {
-	if err = p.SmartBlock.Init(s); err != nil {
+func (p *Archive) Init(s source.Source, _ bool) (err error) {
+	if err = p.SmartBlock.Init(s, true); err != nil {
 		return
 	}
 	return p.init()
diff --git a/core/block/editor/archive_test.go b/core/block/editor/archive_test.go
index 1691c9f55..7937a63e1 100644
--- a/core/block/editor/archive_test.go
+++ b/core/block/editor/archive_test.go
@@ -15,7 +15,7 @@ func TestArchive_Archive(t *testing.T) {
 		c := newCtrl()
 		a := NewArchive(c)
 		a.SmartBlock = smarttest.New("root").AddBlock(simple.New(&model.Block{Id: "root"}))
-		require.NoError(t, a.Init(nil))
+		require.NoError(t, a.Init(nil, true))
 
 		require.NoError(t, a.Archive("1"))
 		require.NoError(t, a.Archive("2"))
@@ -32,7 +32,7 @@ func TestArchive_Archive(t *testing.T) {
 		c := newCtrl()
 		a := NewArchive(c)
 		a.SmartBlock = smarttest.New("root").AddBlock(simple.New(&model.Block{Id: "root"}))
-		require.NoError(t, a.Init(nil))
+		require.NoError(t, a.Init(nil, true))
 
 		require.NoError(t, a.Archive("1"))
 		require.NoError(t, a.Archive("1"))
@@ -48,7 +48,7 @@ func TestArchive_UnArchive(t *testing.T) {
 		c := newCtrl()
 		a := NewArchive(c)
 		a.SmartBlock = smarttest.New("root").AddBlock(simple.New(&model.Block{Id: "root"}))
-		require.NoError(t, a.Init(nil))
+		require.NoError(t, a.Init(nil, true))
 
 		require.NoError(t, a.Archive("1"))
 		require.NoError(t, a.Archive("2"))
@@ -64,7 +64,7 @@ func TestArchive_UnArchive(t *testing.T) {
 		c := newCtrl()
 		a := NewArchive(c)
 		a.SmartBlock = smarttest.New("root").AddBlock(simple.New(&model.Block{Id: "root"}))
-		require.NoError(t, a.Init(nil))
+		require.NoError(t, a.Init(nil, true))
 
 		require.NoError(t, a.Archive("1"))
 
@@ -81,7 +81,7 @@ func TestArchive_Delete(t *testing.T) {
 		c := newCtrl()
 		a := NewArchive(c)
 		a.SmartBlock = smarttest.New("root").AddBlock(simple.New(&model.Block{Id: "root"}))
-		require.NoError(t, a.Init(nil))
+		require.NoError(t, a.Init(nil, true))
 
 		require.NoError(t, a.Archive("1"))
 		require.NoError(t, a.Archive("2"))
diff --git a/core/block/editor/breadcrumbs_test.go b/core/block/editor/breadcrumbs_test.go
index 5a4018fc9..893ad302f 100644
--- a/core/block/editor/breadcrumbs_test.go
+++ b/core/block/editor/breadcrumbs_test.go
@@ -11,7 +11,7 @@ import (
 
 func TestBreadcrumbs_Init(t *testing.T) {
 	b := NewBreadcrumbs()
-	err := b.Init(source.NewVirtual(nil, nil, pb.SmartBlockType_Breadcrumbs))
+	err := b.Init(source.NewVirtual(nil, nil, pb.SmartBlockType_Breadcrumbs), true)
 	require.NoError(t, err)
 	assert.NotEmpty(t, b.Id())
 	assert.NotEmpty(t, b.RootId())
@@ -21,7 +21,7 @@ func TestBreadcrumbs_Init(t *testing.T) {
 func TestBreadcrumbs_SetCrumbs(t *testing.T) {
 	t.Run("set ids", func(t *testing.T) {
 		b := NewBreadcrumbs()
-		err := b.Init(source.NewVirtual(nil, nil, pb.SmartBlockType_Breadcrumbs))
+		err := b.Init(source.NewVirtual(nil, nil, pb.SmartBlockType_Breadcrumbs), true)
 		require.NoError(t, err)
 		require.NoError(t, b.SetCrumbs([]string{"one", "two"}))
 		require.Len(t, b.NewState().Pick(b.RootId()).Model().ChildrenIds, 2)
diff --git a/core/block/editor/clipboard/clipboard.go b/core/block/editor/clipboard/clipboard.go
index 16d66e8e1..0ad62db7f 100644
--- a/core/block/editor/clipboard/clipboard.go
+++ b/core/block/editor/clipboard/clipboard.go
@@ -83,11 +83,17 @@ func (cb *clipboard) Copy(req pb.RpcBlockCopyRequest, images map[string][]byte)
 		return textSlot, htmlSlot, anySlot, nil
 	}
 
+	var firstBlockSelectionLength int32
 	if req.SelectedTextRange.From == 0 && req.SelectedTextRange.To == 0 && req.Blocks[0].GetText() != nil {
 		req.SelectedTextRange.To = int32(utf8.RuneCountInString(req.Blocks[0].GetText().Text))
+		firstBlockSelectionLength = req.SelectedTextRange.To
 	}
 
-	if len(req.Blocks) == 1 && (req.SelectedTextRange == nil || req.SelectedTextRange.From == req.SelectedTextRange.To) {
+	// in case it's the only one block selected and provided selection range is full or empty just return the block from the request
+	if len(req.Blocks) == 1 &&
+		(req.SelectedTextRange == nil ||
+			req.SelectedTextRange.From == req.SelectedTextRange.To ||
+			req.SelectedTextRange.From == 0 && req.SelectedTextRange.To == firstBlockSelectionLength) {
 		return textSlot, htmlSlot, anySlot, nil
 	}
 
diff --git a/core/block/editor/dashboard.go b/core/block/editor/dashboard.go
index 3f423e45f..832b24392 100644
--- a/core/block/editor/dashboard.go
+++ b/core/block/editor/dashboard.go
@@ -29,8 +29,8 @@ type Dashboard struct {
 	_import.Import
 }
 
-func (p *Dashboard) Init(s source.Source) (err error) {
-	if err = p.SmartBlock.Init(s); err != nil {
+func (p *Dashboard) Init(s source.Source, _ bool) (err error) {
+	if err = p.SmartBlock.Init(s, true); err != nil {
 		return
 	}
 	return p.init()
diff --git a/core/block/editor/import/import.go b/core/block/editor/import/import.go
index 201604ab1..03b37c5fa 100644
--- a/core/block/editor/import/import.go
+++ b/core/block/editor/import/import.go
@@ -500,8 +500,10 @@ func (imp *importImpl) DirWithMarkdownToBlocks(importPath string) (files map[str
 					link := txt.Marks.Marks[0].Param
 
 					var wholeLineLink bool
-					if (txt.Marks.Marks[0].Range.From == 0 || strings.TrimSpace(txt.Text[0:txt.Marks.Marks[0].Range.From]) == "") &&
-						(int(txt.Marks.Marks[0].Range.To) >= (len(txt.Text)-1) || strings.TrimSpace(txt.Text[txt.Marks.Marks[0].Range.To:]) == "") {
+					textRunes := []rune(txt.Text)
+
+					if (txt.Marks.Marks[0].Range.From == 0 || strings.TrimSpace(string(textRunes[0:txt.Marks.Marks[0].Range.From])) == "") &&
+						(int(txt.Marks.Marks[0].Range.To) >= (len(textRunes)-1) || strings.TrimSpace(string(textRunes[txt.Marks.Marks[0].Range.To:])) == "") {
 						wholeLineLink = true
 					}
 
@@ -627,21 +629,29 @@ func (imp *importImpl) convertTextToPageMention(block *model.Block) {
 
 func (imp *importImpl) convertTextToFile(block *model.Block) {
 	// "svg" excluded
+	if block.GetText().Marks.Marks[0].Param == "" {
+		return
+	}
+
 	imageFormats := []string{"jpg", "jpeg", "png", "gif", "webp"}
 	videoFormats := []string{"mp4", "m4v"}
 
 	fileType := model.BlockContentFile_File
-	for _, ext := range imageFormats {
-		if strings.EqualFold(filepath.Ext(block.GetText().Marks.Marks[0].Param)[1:], ext) {
-			fileType = model.BlockContentFile_Image
-			break
+	fileExt := filepath.Ext(block.GetText().Marks.Marks[0].Param)
+	if fileExt != "" {
+		fileExt = fileExt[1:]
+		for _, ext := range imageFormats {
+			if strings.EqualFold(fileExt, ext) {
+				fileType = model.BlockContentFile_Image
+				break
+			}
 		}
-	}
 
-	for _, ext := range videoFormats {
-		if strings.EqualFold(filepath.Ext(block.GetText().Marks.Marks[0].Param)[1:], ext) {
-			fileType = model.BlockContentFile_Video
-			break
+		for _, ext := range videoFormats {
+			if strings.EqualFold(fileExt, ext) {
+				fileType = model.BlockContentFile_Video
+				break
+			}
 		}
 	}
 
diff --git a/core/block/editor/profile.go b/core/block/editor/profile.go
index bcd691669..9581a5b22 100644
--- a/core/block/editor/profile.go
+++ b/core/block/editor/profile.go
@@ -7,6 +7,7 @@ import (
 	"github.com/anytypeio/go-anytype-middleware/core/block/editor/file"
 	"github.com/anytypeio/go-anytype-middleware/core/block/editor/smartblock"
 	"github.com/anytypeio/go-anytype-middleware/core/block/editor/stext"
+	"github.com/anytypeio/go-anytype-middleware/core/block/source"
 	"github.com/anytypeio/go-anytype-middleware/pb"
 	"github.com/anytypeio/go-anytype-middleware/util/linkpreview"
 )
@@ -37,6 +38,10 @@ type Profile struct {
 	sendEvent func(e *pb.Event)
 }
 
+func (p *Profile) Init(s source.Source, _ bool) (err error) {
+	return p.SmartBlock.Init(s, true)
+}
+
 func (p *Profile) SetDetails(details []*pb.RpcBlockSetDetailsDetail) (err error) {
 	if err = p.SmartBlock.SetDetails(details); err != nil {
 		return
diff --git a/core/block/editor/smartblock/smartblock.go b/core/block/editor/smartblock/smartblock.go
index d25f0bbe1..ac3ee6f7d 100644
--- a/core/block/editor/smartblock/smartblock.go
+++ b/core/block/editor/smartblock/smartblock.go
@@ -44,7 +44,7 @@ type SmartblockOpenListner interface {
 }
 
 type SmartBlock interface {
-	Init(s source.Source) (err error)
+	Init(s source.Source, allowEmpty bool) (err error)
 	Id() string
 	Type() pb.SmartBlockType
 	Meta() *core.SmartBlockMeta
@@ -89,13 +89,9 @@ func (sb *smartBlock) Type() pb.SmartBlockType {
 	return sb.source.Type()
 }
 
-func (sb *smartBlock) Init(s source.Source) error {
-	ver, err := s.ReadVersion()
-	if err != nil && err != core.ErrBlockSnapshotNotFound {
-		return err
-	}
+func (sb *smartBlock) init(s source.Source, ver *core.SmartBlockVersion) error {
 	var blocks = make(map[string]simple.Block)
-	if err == nil {
+	if ver != nil {
 		models, e := ver.Snapshot.Blocks()
 		if e != nil {
 			return e
@@ -121,6 +117,15 @@ func (sb *smartBlock) Init(s source.Source) error {
 	return sb.checkRootBlock()
 }
 
+func (sb *smartBlock) Init(s source.Source, allowEmpty bool) error {
+	ver, err := s.ReadVersion()
+	if err != nil && (err != core.ErrBlockSnapshotNotFound || !allowEmpty) {
+		return err
+	}
+
+	return sb.init(s, ver)
+}
+
 func (sb *smartBlock) checkRootBlock() (err error) {
 	s := sb.NewState()
 	if root := s.Get(sb.RootId()); root != nil {
diff --git a/core/block/editor/smartblock/smartblock_test.go b/core/block/editor/smartblock/smartblock_test.go
index fe3e5913c..507117905 100644
--- a/core/block/editor/smartblock/smartblock_test.go
+++ b/core/block/editor/smartblock/smartblock_test.go
@@ -144,6 +144,6 @@ func (fx *fixture) init(blocks []*model.Block) {
 	fx.source.EXPECT().Id().Return(blocks[0].Id).AnyTimes()
 	fx.snapshot.EXPECT().Blocks().Return(blocks, nil)
 
-	err := fx.Init(fx.source)
+	err := fx.Init(fx.source, true)
 	require.NoError(fx.t, err)
 }
diff --git a/core/block/editor/smartblock/smarttest/smarttest.go b/core/block/editor/smartblock/smarttest/smarttest.go
index 5b11d10ab..8beaed3e5 100644
--- a/core/block/editor/smartblock/smarttest/smarttest.go
+++ b/core/block/editor/smartblock/smarttest/smarttest.go
@@ -52,7 +52,7 @@ func (st *SmartTest) SetDetails(details []*pb.RpcBlockSetDetailsDetail) (err err
 	return
 }
 
-func (st *SmartTest) Init(_ source.Source) (err error) {
+func (st *SmartTest) Init(_ source.Source, _ bool) (err error) {
 	return
 }
 
diff --git a/core/block/service.go b/core/block/service.go
index 112562884..4056c3835 100644
--- a/core/block/service.go
+++ b/core/block/service.go
@@ -186,7 +186,7 @@ func (s *service) OpenBlock(ctx *state.Context, id string) (err error) {
 	defer s.m.Unlock()
 	ob, ok := s.openedBlocks[id]
 	if !ok {
-		sb, e := s.createSmartBlock(id)
+		sb, e := s.createSmartBlock(id, false)
 		if e != nil {
 			return e
 		}
@@ -216,7 +216,7 @@ func (s *service) OpenBreadcrumbsBlock(ctx *state.Context) (blockId string, err
 	s.m.Lock()
 	defer s.m.Unlock()
 	bs := editor.NewBreadcrumbs()
-	if err = bs.Init(source.NewVirtual(s.anytype, s.meta, pb.SmartBlockType_Breadcrumbs)); err != nil {
+	if err = bs.Init(source.NewVirtual(s.anytype, s.meta, pb.SmartBlockType_Breadcrumbs), true); err != nil {
 		return
 	}
 	bs.Lock()
@@ -361,6 +361,11 @@ func (s *service) CreateSmartBlock(req pb.RpcBlockCreatePageRequest) (pageId str
 		return
 	}
 	pageId = csm.ID()
+
+	if _, err = s.createSmartBlock(pageId, true); err != nil {
+		return pageId, err
+	}
+
 	log.Infof("created new smartBlock: %v", pageId)
 	if req.Details != nil && req.Details.Fields != nil {
 		var details []*pb.RpcBlockSetDetailsDetail
@@ -838,7 +843,7 @@ func (s *service) pickBlock(id string) (sb smartblock.SmartBlock, release func()
 	}
 	ob, ok := s.openedBlocks[id]
 	if !ok {
-		sb, err = s.createSmartBlock(id)
+		sb, err = s.createSmartBlock(id, false)
 		if err != nil {
 			return
 		}
@@ -856,7 +861,7 @@ func (s *service) pickBlock(id string) (sb smartblock.SmartBlock, release func()
 	}, nil
 }
 
-func (s *service) createSmartBlock(id string) (sb smartblock.SmartBlock, err error) {
+func (s *service) createSmartBlock(id string, initEmpty bool) (sb smartblock.SmartBlock, err error) {
 	sc, err := source.NewSource(s.anytype, s.meta, id)
 	if err != nil {
 		return
@@ -876,9 +881,10 @@ func (s *service) createSmartBlock(id string) (sb smartblock.SmartBlock, err err
 		return nil, fmt.Errorf("unexpected smartblock type: %v", sc.Type())
 	}
 
-	if err = sb.Init(sc); err != nil {
+	if err = sb.Init(sc, initEmpty); err != nil {
 		return
 	}
+
 	return
 }
 
diff --git a/go.mod b/go.mod
index ce3290696..21d7e5783 100644
--- a/go.mod
+++ b/go.mod
@@ -10,19 +10,21 @@ require (
 	github.com/gogo/protobuf v1.3.1
 	github.com/golang/mock v1.4.3
 	github.com/google/uuid v1.1.1
-	github.com/h2non/filetype v1.0.12
+	github.com/h2non/filetype v1.1.0
 	github.com/hashicorp/golang-lru v0.5.4
 	github.com/improbable-eng/grpc-web v0.12.0
 	github.com/mauidude/go-readability v0.0.0-20141216012317-2f30b1a346f1
-	github.com/microcosm-cc/bluemonday v1.0.2
+	github.com/microcosm-cc/bluemonday v1.0.3
 	github.com/otiai10/opengraph v1.1.1
 	github.com/santhosh-tekuri/jsonschema/v2 v2.2.0
-	github.com/stretchr/testify v1.5.1
+	github.com/stretchr/testify v1.6.1
 	github.com/yosssi/gohtml v0.0.0-20190915184251-7ff6f235ecaf
 	github.com/yuin/goldmark v1.1.30
 	golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect
-	golang.org/x/text v0.3.2
-	google.golang.org/grpc v1.29.0
+	golang.org/x/text v0.3.3
+	google.golang.org/grpc v1.29.1
 )
 
-replace github.com/JohannesKaufmann/html-to-markdown => github.com/anytypeio/html-to-markdown v0.0.0-20200604073925-3e04de035205
+replace github.com/JohannesKaufmann/html-to-markdown => github.com/anytypeio/html-to-markdown v0.0.0-20200617145221-2afd2a14bae1
+
+replace github.com/textileio/go-threads => github.com/anytypeio/go-threads v0.1.18-0.20200612135054-98cf128fb49f
diff --git a/go.sum b/go.sum
index a5c00c4f0..452cbbe5d 100644
--- a/go.sum
+++ b/go.sum
@@ -20,18 +20,20 @@ github.com/alecthomas/jsonschema v0.0.0-20191017121752-4bb6e3fae4f2 h1:swGeCLPiU
 github.com/alecthomas/jsonschema v0.0.0-20191017121752-4bb6e3fae4f2/go.mod h1:Juc2PrI3wtNfUwptSvAIeNx+HrETwHQs6nf+TkOJlOA=
 github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
 github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
-github.com/anytypeio/go-anytype-library v0.8.1-0.20200619071256-b708ecb8bcdb h1:5V9ZAwI2r4XJKayjI/I4r/voHbeQcrJHwLzbBrMZ9JY=
-github.com/anytypeio/go-anytype-library v0.8.1-0.20200619071256-b708ecb8bcdb/go.mod h1:vKJTNxz5arl5baUC1yiovFqvXzcfdnoNhWfUlhaYTz0=
 github.com/anytypeio/go-anytype-library v0.8.1-0.20200619133751-8bc35c364ada h1:I1NbGFopypSwMOubcFgtKMKBsdD+vFd6RE5uatTYoSE=
 github.com/anytypeio/go-anytype-library v0.8.1-0.20200619133751-8bc35c364ada/go.mod h1:vKJTNxz5arl5baUC1yiovFqvXzcfdnoNhWfUlhaYTz0=
 github.com/anytypeio/go-slip10 v0.0.0-20200330112030-a352ca8495e4 h1:jB5Ke7NVoW52i65PtLFBr5Q5k6RskIY8L70pgnBcnWo=
 github.com/anytypeio/go-slip10 v0.0.0-20200330112030-a352ca8495e4/go.mod h1:/8GIEJBE5wmdgcE49JPdupnHNUf7bEn6C+aArfWqvw8=
 github.com/anytypeio/go-slip21 v0.0.0-20200218204727-e2e51e20ab51 h1:3Y+18zBC8LZgcL3l2dgoTEIzIUzCZa/kN0UV3ZWpbuA=
 github.com/anytypeio/go-slip21 v0.0.0-20200218204727-e2e51e20ab51/go.mod h1:SoKy+W8Mf6v7XBV30xFWkIFMs7UnXwsNGrGV12yVkEs=
-github.com/anytypeio/html-to-markdown v0.0.0-20200604073925-3e04de035205 h1:AN38Yo6CCO3LQI3OOH5YTPa8R4DGV/uveuAqcj029IY=
-github.com/anytypeio/html-to-markdown v0.0.0-20200604073925-3e04de035205/go.mod h1:Qnhxlb4mi8T2624UtHX8EgDyYZXWbjQfLGuciFHZ+Go=
+github.com/anytypeio/go-threads v0.1.18-0.20200612135054-98cf128fb49f h1:8SoiQ8XPEm8+ahpOo4DembBqTYWy3o2TmuN5oQsqnMU=
+github.com/anytypeio/go-threads v0.1.18-0.20200612135054-98cf128fb49f/go.mod h1:lpSnvxq8qz1uCCClDk3g5cnZusHsLeMmY7Xr64v7LIw=
+github.com/anytypeio/html-to-markdown v0.0.0-20200617145221-2afd2a14bae1 h1:g/LEIEQ0ACBOKX9MhORhlmluUKvuxvrIDbGMI0cqF5A=
+github.com/anytypeio/html-to-markdown v0.0.0-20200617145221-2afd2a14bae1/go.mod h1:Qnhxlb4mi8T2624UtHX8EgDyYZXWbjQfLGuciFHZ+Go=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
 github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
 github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
 github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
@@ -54,6 +56,8 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cheggaaa/mb v1.0.2 h1:YZO20cG1uCyIsBxxZyo9fvaqyVlCD94VQhnjmjcOaW4=
 github.com/cheggaaa/mb v1.0.2/go.mod h1:GLKG3DLryeF3F4iC+5b5mSjDq5o7skhK2qGWEOzF6g8=
+github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
+github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -151,6 +155,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
 github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
 github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -163,6 +169,8 @@ github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmv
 github.com/gxed/pubsub v0.0.0-20180201040156-26ebdf44f824/go.mod h1:OiEWyHgK+CWrmOlVquHaIK1vhpUJydC9m0Je6mhaiNE=
 github.com/h2non/filetype v1.0.12 h1:yHCsIe0y2cvbDARtJhGBTD2ecvqMSTvlIcph9En/Zao=
 github.com/h2non/filetype v1.0.12/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
+github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA=
+github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
@@ -677,8 +685,8 @@ github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvr
 github.com/mauidude/go-readability v0.0.0-20141216012317-2f30b1a346f1 h1:LUX7+Xw9WqBYU1KIhBeHhE9IEziRmfE6QL/KOJw27XY=
 github.com/mauidude/go-readability v0.0.0-20141216012317-2f30b1a346f1/go.mod h1:JunVAey+5bzS6oFzfAEXL15REaSvzaDhB+muny1oSNU=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
-github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
+github.com/microcosm-cc/bluemonday v1.0.3 h1:EjVH7OqbU219kdm8acbveoclh2zZFqPJTJw6VUlTLAQ=
+github.com/microcosm-cc/bluemonday v1.0.3/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
 github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/miekg/dns v1.1.12 h1:WMhc1ik4LNkTg8U9l3hI1LvxKmIL+f1+WV/SZtCbDDA=
 github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -752,6 +760,7 @@ github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS
 github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
 github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg=
 github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo=
 github.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc=
@@ -841,10 +850,10 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
 github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
-github.com/textileio/go-threads v0.1.18-0.20200513233611-948f69e7988f h1:ptDvHfXZ/ONG3SmLmxOcT8o9U8CQ8QoT4djXNhuiJGg=
-github.com/textileio/go-threads v0.1.18-0.20200513233611-948f69e7988f/go.mod h1:lpSnvxq8qz1uCCClDk3g5cnZusHsLeMmY7Xr64v7LIw=
 github.com/tidwall/gjson v1.3.5 h1:2oW9FBNu8qt9jy5URgrzsVx/T/KSn3qn/smJQ0crlDQ=
 github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
 github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
@@ -1011,6 +1020,8 @@ golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fq
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1051,6 +1062,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.29.0 h1:2pJjwYOdkZ9HlN4sWRYBg9ttH5bCOlsueaM+b/oYjwo=
 google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1070,6 +1083,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=