Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
www
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
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
PLN
www
Commits
a3812f69
Commit
a3812f69
authored
Sep 14, 2025
by
PLN (Algolia)
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Dunbar: NLP PAss
parent
fbdcaf0f
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
198 additions
and
6 deletions
+198
-6
DunbarApp.js
next/components/dunbar/DunbarApp.js
+8
-2
EventsTab.js
next/components/dunbar/EventsTab.js
+61
-1
FriendsList.js
next/components/dunbar/FriendsList.js
+21
-0
NetworkTab.js
next/components/dunbar/NetworkTab.js
+0
-0
SearchTab.js
next/components/dunbar/SearchTab.js
+32
-2
StatsTab.js
next/components/dunbar/StatsTab.js
+68
-1
layout.js
next/components/layout.js
+8
-0
index.js
next/lib/dunbar-nlp/index.js
+0
-0
No files found.
next/components/dunbar/DunbarApp.js
View file @
a3812f69
...
@@ -141,7 +141,6 @@ export default function DunbarApp() {
...
@@ -141,7 +141,6 @@ export default function DunbarApp() {
router
.
push
(
`/dunbar/event/
${
eventSlug
(
e
)}
`
,
undefined
,
{
shallow
:
true
});
router
.
push
(
`/dunbar/event/
${
eventSlug
(
e
)}
`
,
undefined
,
{
shallow
:
true
});
};
};
if
(
!
authed
)
{
// Deep-link handling: friend/event/search routes hydrate initial tab/selection
// Deep-link handling: friend/event/search routes hydrate initial tab/selection
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
!
router
||
!
router
.
asPath
)
return
;
if
(
!
router
||
!
router
.
asPath
)
return
;
...
@@ -180,6 +179,8 @@ export default function DunbarApp() {
...
@@ -180,6 +179,8 @@ export default function DunbarApp() {
}
}
},
[
router
?.
asPath
,
friends
,
derived
.
eventIndex
,
actions
]);
},
[
router
?.
asPath
,
friends
,
derived
.
eventIndex
,
actions
]);
if
(
!
authed
)
{
return
(
return
(
<
div
className
=
{
styles
.
lockWrap
}
>
<
div
className
=
{
styles
.
lockWrap
}
>
<
h2
className
=
{
styles
.
title
}
>
Dunbar
<
/h2
>
<
h2
className
=
{
styles
.
title
}
>
Dunbar
<
/h2
>
...
@@ -283,7 +284,12 @@ export default function DunbarApp() {
...
@@ -283,7 +284,12 @@ export default function DunbarApp() {
)}
)}
{
tab
===
'stats'
&&
(
{
tab
===
'stats'
&&
(
<
StatsTab
stats
=
{
derived
.
stats
}
anniversaries
=
{
derived
.
anniversaries
}
openFriend
=
{
openFriendDetail
}
/
>
<
StatsTab
stats
=
{
derived
.
stats
}
anniversaries
=
{
derived
.
anniversaries
}
eventIndex
=
{
derived
.
eventIndex
}
openFriend
=
{
openFriendDetail
}
/
>
)}
)}
<
/div
>
<
/div
>
);
);
...
...
next/components/dunbar/EventsTab.js
View file @
a3812f69
...
@@ -9,6 +9,7 @@ import {
...
@@ -9,6 +9,7 @@ import {
isoDate
,
isoDate
,
}
from
'@/lib/dunbar'
;
}
from
'@/lib/dunbar'
;
import
{
extractTags
}
from
'@/lib/dunbar'
;
import
{
extractTags
}
from
'@/lib/dunbar'
;
import
{
detectLang
,
topKeywordsForDocs
,
extractTopics
}
from
'@/lib/dunbar-nlp'
;
export
default
function
EventsTab
({
friends
,
addEvent
,
updateEvent
,
selectedEventId
,
eventIndex
,
openEvent
})
{
export
default
function
EventsTab
({
friends
,
addEvent
,
updateEvent
,
selectedEventId
,
eventIndex
,
openEvent
})
{
// Creation form state
// Creation form state
...
@@ -58,6 +59,36 @@ export default function EventsTab({ friends, addEvent, updateEvent, selectedEven
...
@@ -58,6 +59,36 @@ export default function EventsTab({ friends, addEvent, updateEvent, selectedEven
// Timeline groups from merged eventIndex
// Timeline groups from merged eventIndex
const
groups
=
useMemo
(()
=>
groupEventsByDay
(
eventIndex
),
[
eventIndex
]);
const
groups
=
useMemo
(()
=>
groupEventsByDay
(
eventIndex
),
[
eventIndex
]);
// NLP: keywords per event (TF-IDF over corpus) and language guess
const
keywordData
=
useMemo
(()
=>
{
const
docs
=
(
eventIndex
||
[]).
map
((
e
)
=>
({
id
:
e
.
id
,
text
:
`
${
e
.
title
||
''
}
${
e
.
notes
||
''
}
`
,
}));
const
corpusText
=
docs
.
map
((
d
)
=>
d
.
text
).
join
(
' '
);
const
lang
=
detectLang
(
corpusText
)
||
null
;
const
top
=
topKeywordsForDocs
(
docs
,
{
lang
,
topK
:
6
});
const
byId
=
new
Map
(
top
.
map
((
d
)
=>
[
d
.
id
,
d
.
keywords
]));
return
{
byId
,
lang
};
},
[
eventIndex
]);
// Topics (beta) — computed on demand
const
[
topics
,
setTopics
]
=
useState
([]);
const
[
topicsLoading
,
setTopicsLoading
]
=
useState
(
false
);
const
runTopics
=
async
()
=>
{
try
{
setTopicsLoading
(
true
);
const
docs
=
(
eventIndex
||
[]).
map
((
e
)
=>
({
id
:
e
.
id
,
text
:
`
${
e
.
title
||
''
}
${
e
.
notes
||
''
}
`
,
}));
const
res
=
await
extractTopics
(
docs
,
{
topics
:
5
,
termsPerTopic
:
6
,
lang
:
keywordData
.
lang
||
null
});
setTopics
(
res
);
}
finally
{
setTopicsLoading
(
false
);
}
};
// Selected event editor state
// Selected event editor state
const
selectedEvent
=
useMemo
(
const
selectedEvent
=
useMemo
(
()
=>
(
selectedEventId
?
(
eventIndex
||
[]).
find
((
e
)
=>
e
.
id
===
selectedEventId
)
:
null
),
()
=>
(
selectedEventId
?
(
eventIndex
||
[]).
find
((
e
)
=>
e
.
id
===
selectedEventId
)
:
null
),
...
@@ -219,7 +250,26 @@ export default function EventsTab({ friends, addEvent, updateEvent, selectedEven
...
@@ -219,7 +250,26 @@ export default function EventsTab({ friends, addEvent, updateEvent, selectedEven
{
/* Timeline + Editor */
}
{
/* Timeline + Editor */
}
<
div
className
=
{
styles
.
card
}
>
<
div
className
=
{
styles
.
card
}
>
<
div
className
=
{
styles
.
cardHeader
}
>
Timeline
<
/div
>
<
div
className
=
{
styles
.
cardHeader
}
>
<
span
>
Timeline
<
/span
>
<
button
className
=
{
styles
.
btnSecondary
}
onClick
=
{
runTopics
}
disabled
=
{
topicsLoading
||
!
(
eventIndex
||
[]).
length
}
title
=
"Compute topics from titles+notes (local)"
>
{
topicsLoading
?
'Topics…'
:
'Topics (beta)'
}
<
/button
>
<
/div
>
{
topics
&&
topics
.
length
>
0
?
(
<
div
className
=
{
styles
.
tagRow
}
style
=
{{
margin
:
'8px 0'
}}
>
{
topics
.
map
((
t
,
i
)
=>
(
<
span
key
=
{
i
}
className
=
{
styles
.
tagChip
}
title
=
{
t
.
terms
.
map
(([
term
])
=>
term
).
join
(
', '
)}
>
Topic
{
i
+
1
}:
{
t
.
terms
.
slice
(
0
,
3
).
map
(([
term
])
=>
term
).
join
(
' / '
)}
<
/span
>
))}
<
/div
>
)
:
null
}
{
/* Event Editor */
}
{
/* Event Editor */
}
{
selectedEvent
?
(
{
selectedEvent
?
(
...
@@ -329,6 +379,16 @@ export default function EventsTab({ friends, addEvent, updateEvent, selectedEven
...
@@ -329,6 +379,16 @@ export default function EventsTab({ friends, addEvent, updateEvent, selectedEven
<
/div
>
<
/div
>
)
:
null
;
)
:
null
;
})()}
})()}
{(()
=>
{
const
kws
=
keywordData
.
byId
.
get
(
e
.
id
)
||
[];
return
kws
.
length
?
(
<
div
className
=
{
styles
.
tagRow
}
style
=
{{
marginTop
:
4
}}
>
{
kws
.
slice
(
0
,
6
).
map
(([
term
])
=>
(
<
span
key
=
{
term
}
className
=
{
styles
.
tagChip
}
>
{
term
}
<
/span
>
))}
<
/div
>
)
:
null
;
})()}
<
div
style
=
{{
color
:
'#888'
,
marginTop
:
4
,
fontSize
:
12
}}
>
<
div
style
=
{{
color
:
'#888'
,
marginTop
:
4
,
fontSize
:
12
}}
>
{
isoDate
(
e
.
date
)}
{
isoDate
(
e
.
date
)}
<
/div
>
<
/div
>
...
...
next/components/dunbar/FriendsList.js
View file @
a3812f69
import
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
'react'
;
import
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
'react'
;
import
styles
from
'@/styles/dunbar.module.css'
;
import
styles
from
'@/styles/dunbar.module.css'
;
import
{
extractLocations
}
from
'@/lib/dunbar-nlp'
;
export
default
function
FriendsList
({
export
default
function
FriendsList
({
friends
,
friends
,
...
@@ -35,6 +36,22 @@ export default function FriendsList({
...
@@ -35,6 +36,22 @@ export default function FriendsList({
return
friends
.
filter
((
f
)
=>
f
.
name
.
toLowerCase
().
includes
(
q
));
return
friends
.
filter
((
f
)
=>
f
.
name
.
toLowerCase
().
includes
(
q
));
},
[
friends
,
filter
]);
},
[
friends
,
filter
]);
// Offline location mentions per friend (from notes + events)
const
locByFriend
=
useMemo
(()
=>
{
const
m
=
new
Map
();
for
(
const
f
of
friends
)
{
let
text
=
''
;
text
+=
' '
+
(
f
.
notes
||
''
);
for
(
const
ev
of
f
.
events
||
[])
{
text
+=
' '
+
(
ev
.
title
||
''
)
+
' '
+
(
ev
.
notes
||
''
)
+
' '
+
(
ev
.
location
||
''
);
}
const
locs
=
extractLocations
(
text
).
map
((
l
)
=>
l
.
name
);
const
uniq
=
Array
.
from
(
new
Set
(
locs
));
m
.
set
(
f
.
id
,
uniq
);
}
return
m
;
},
[
friends
]);
const
handleAdd
=
()
=>
{
const
handleAdd
=
()
=>
{
const
n
=
name
.
trim
();
const
n
=
name
.
trim
();
if
(
!
n
)
return
;
if
(
!
n
)
return
;
...
@@ -143,6 +160,10 @@ export default function FriendsList({
...
@@ -143,6 +160,10 @@ export default function FriendsList({
)}
)}
<
div
className
=
{
styles
.
itemMeta
}
>
<
div
className
=
{
styles
.
itemMeta
}
>
&
nbsp
;
·
&
nbsp
;{
evCount
}
events
·
{
connCount
}
connections
&
nbsp
;
·
&
nbsp
;{
evCount
}
events
·
{
connCount
}
connections
{(()
=>
{
const
locs
=
locByFriend
.
get
(
f
.
id
)
||
[];
return
locs
.
length
?
<>
·
📍
{
locs
.
slice
(
0
,
2
).
join
(
', '
)}
<
/> : null
;
})()}
<
/div
>
<
/div
>
<
div
className
=
{
styles
.
itemRight
}
aria
-
hidden
>
›
<
/div
>
<
div
className
=
{
styles
.
itemRight
}
aria
-
hidden
>
›
<
/div
>
<
button
<
button
...
...
next/components/dunbar/NetworkTab.js
View file @
a3812f69
This diff is collapsed.
Click to expand it.
next/components/dunbar/SearchTab.js
View file @
a3812f69
...
@@ -7,6 +7,7 @@ import {
...
@@ -7,6 +7,7 @@ import {
suggestTags
,
suggestTags
,
suggestPersons
,
suggestPersons
,
}
from
'@/lib/dunbar-search'
;
}
from
'@/lib/dunbar-search'
;
import
{
tokenize
}
from
'@/lib/dunbar-nlp'
;
export
default
function
SearchTab
({
friends
,
openFriend
,
openEvent
})
{
export
default
function
SearchTab
({
friends
,
openFriend
,
openEvent
})
{
const
[
q
,
setQ
]
=
useState
(
''
);
const
[
q
,
setQ
]
=
useState
(
''
);
...
@@ -76,6 +77,35 @@ export default function SearchTab({ friends, openFriend, openEvent }) {
...
@@ -76,6 +77,35 @@ export default function SearchTab({ friends, openFriend, openEvent }) {
return
<>
{
parts
}
<
/>
;
return
<>
{
parts
}
<
/>
;
};
};
// Query tokens for highlight (non-hashtag, length>=3)
const
queryTokens
=
useMemo
(
()
=>
tokenize
(
q
||
''
,
{
keepHashtags
:
true
,
removeDiacritics
:
true
}).
filter
(
(
t
)
=>
!
t
.
startsWith
(
'#'
)
&&
t
.
length
>=
3
),
[
q
]
);
const
escapeRegExp
=
(
s
)
=>
s
.
replace
(
/
[
.*+?^${}()|[
\]\\]
/g
,
'
\\
$&'
);
const
renderHighlight
=
(
text
=
''
,
tokens
=
[])
=>
{
if
(
!
tokens
||
tokens
.
length
===
0
)
return
text
;
const
pattern
=
new
RegExp
(
`(
${
tokens
.
map
(
escapeRegExp
).
join
(
'|'
)}
)`
,
'gi'
);
const
parts
=
String
(
text
).
split
(
pattern
);
const
tokenSet
=
new
Set
(
tokens
.
map
((
t
)
=>
t
.
toLowerCase
()));
return
(
<>
{
parts
.
map
((
part
,
i
)
=>
tokenSet
.
has
(
String
(
part
).
toLowerCase
())
?
(
<
mark
key
=
{
i
}
style
=
{{
backgroundColor
:
'#fff2a8'
,
padding
:
'0 2px'
}}
>
{
part
}
<
/mark
>
)
:
(
<
span
key
=
{
i
}
>
{
part
}
<
/span
>
)
)}
<
/
>
);
};
const
tagSuggestions
=
useMemo
(()
=>
(
indexes
?
suggestTags
(
indexes
,
q
)
:
[]),
[
indexes
,
q
]);
const
tagSuggestions
=
useMemo
(()
=>
(
indexes
?
suggestTags
(
indexes
,
q
)
:
[]),
[
indexes
,
q
]);
const
personSuggestions
=
useMemo
(()
=>
(
indexes
?
suggestPersons
(
indexes
,
q
)
:
[]),
[
indexes
,
q
]);
const
personSuggestions
=
useMemo
(()
=>
(
indexes
?
suggestPersons
(
indexes
,
q
)
:
[]),
[
indexes
,
q
]);
...
@@ -162,7 +192,7 @@ export default function SearchTab({ friends, openFriend, openEvent }) {
...
@@ -162,7 +192,7 @@ export default function SearchTab({ friends, openFriend, openEvent }) {
<
div
className
=
{
styles
.
listScroll
}
style
=
{{
maxHeight
:
'50vh'
}}
>
<
div
className
=
{
styles
.
listScroll
}
style
=
{{
maxHeight
:
'50vh'
}}
>
{(
results
.
friends
||
[]).
map
((
f
)
=>
(
{(
results
.
friends
||
[]).
map
((
f
)
=>
(
<
div
key
=
{
f
.
id
}
className
=
{
styles
.
listItem
}
onClick
=
{()
=>
openFriend
?.(
f
.
refId
)}
>
<
div
key
=
{
f
.
id
}
className
=
{
styles
.
listItem
}
onClick
=
{()
=>
openFriend
?.(
f
.
refId
)}
>
<
div
className
=
{
styles
.
itemTitle
}
>
{
f
.
name
}
<
/div
>
<
div
className
=
{
styles
.
itemTitle
}
>
{
renderHighlight
(
f
.
name
,
queryTokens
)
}
<
/div
>
{(
f
.
tags
&&
f
.
tags
.
length
)
?
(
{(
f
.
tags
&&
f
.
tags
.
length
)
?
(
<
div
className
=
{
styles
.
tagRow
}
>
<
div
className
=
{
styles
.
tagRow
}
>
{
f
.
tags
.
slice
(
0
,
8
).
map
((
t
)
=>
(
{
f
.
tags
.
slice
(
0
,
8
).
map
((
t
)
=>
(
...
@@ -188,7 +218,7 @@ export default function SearchTab({ friends, openFriend, openEvent }) {
...
@@ -188,7 +218,7 @@ export default function SearchTab({ friends, openFriend, openEvent }) {
onClick
=
{()
=>
openEvent
?.(
e
)}
onClick
=
{()
=>
openEvent
?.(
e
)}
title
=
"Ouvrir l’événement"
title
=
"Ouvrir l’événement"
>
>
{
e
.
title
||
'(untitled)'
}
{
renderHighlight
(
e
.
title
||
'(untitled)'
,
queryTokens
)
}
<
/div
>
<
/div
>
<
div
style
=
{{
whiteSpace
:
'pre-wrap'
}}
>
<
div
style
=
{{
whiteSpace
:
'pre-wrap'
}}
>
{
renderNotesWithTags
(
e
.
notes
||
''
)}
{
renderNotesWithTags
(
e
.
notes
||
''
)}
...
...
next/components/dunbar/StatsTab.js
View file @
a3812f69
import
React
from
'react'
;
import
React
from
'react'
;
import
styles
from
'@/styles/dunbar.module.css'
;
import
styles
from
'@/styles/dunbar.module.css'
;
import
{
isoDate
}
from
'@/lib/dunbar'
;
import
{
isoDate
}
from
'@/lib/dunbar'
;
import
{
detectLang
,
topKeywordsForDocs
,
extractLocations
}
from
'@/lib/dunbar-nlp'
;
export
default
function
StatsTab
({
stats
,
anniversaries
=
[],
openFriend
})
{
export
default
function
StatsTab
({
stats
,
anniversaries
=
[],
eventIndex
=
[],
openFriend
})
{
if
(
!
stats
)
return
null
;
if
(
!
stats
)
return
null
;
const
items
=
[
const
items
=
[
{
label
:
'Connections'
,
value
:
stats
.
connections
},
{
label
:
'Connections'
,
value
:
stats
.
connections
},
...
@@ -11,6 +12,46 @@ export default function StatsTab({ stats, anniversaries = [], openFriend }) {
...
@@ -11,6 +12,46 @@ export default function StatsTab({ stats, anniversaries = [], openFriend }) {
{
label
:
'Avg Events / Friend'
,
value
:
stats
.
avgEventsPerFriend
},
{
label
:
'Avg Events / Friend'
,
value
:
stats
.
avgEventsPerFriend
},
];
];
// Aggregate text insights (local-only): top keywords and locations across all events
const
textInsights
=
React
.
useMemo
(()
=>
{
const
docs
=
(
eventIndex
||
[]).
map
((
e
)
=>
({
id
:
e
.
id
,
text
:
`
${
e
.
title
||
''
}
${
e
.
notes
||
''
}
${
e
.
location
||
''
}
`
,
}));
if
(
!
docs
.
length
)
return
{
topTerms
:
[],
topLocations
:
[]
};
const
corpusText
=
docs
.
map
((
d
)
=>
d
.
text
).
join
(
' '
);
const
lang
=
detectLang
(
corpusText
)
||
null
;
// Aggregate keywords by summing TF-IDF heads across docs
const
perDoc
=
topKeywordsForDocs
(
docs
,
{
lang
,
topK
:
8
});
const
agg
=
new
Map
();
for
(
const
d
of
perDoc
)
{
for
(
const
[
term
,
score
]
of
d
.
keywords
)
{
agg
.
set
(
term
,
(
agg
.
get
(
term
)
||
0
)
+
score
);
}
}
const
topTerms
=
Array
.
from
(
agg
.
entries
())
.
sort
((
a
,
b
)
=>
b
[
1
]
-
a
[
1
])
.
slice
(
0
,
12
)
.
map
(([
term
,
score
])
=>
({
term
,
score
}));
// Count location mentions (unique per event)
const
locCounts
=
new
Map
();
for
(
const
e
of
eventIndex
||
[])
{
const
locs
=
extractLocations
(
`
${
e
.
title
||
''
}
${
e
.
notes
||
''
}
${
e
.
location
||
''
}
`
).
map
((
l
)
=>
l
.
name
);
for
(
const
name
of
new
Set
(
locs
))
{
locCounts
.
set
(
name
,
(
locCounts
.
get
(
name
)
||
0
)
+
1
);
}
}
const
topLocations
=
Array
.
from
(
locCounts
.
entries
())
.
sort
((
a
,
b
)
=>
b
[
1
]
-
a
[
1
])
.
slice
(
0
,
8
)
.
map
(([
name
,
count
])
=>
({
name
,
count
}));
return
{
topTerms
,
topLocations
};
},
[
eventIndex
]);
// Group upcoming anniversaries by date (YYYY-MM-DD)
// Group upcoming anniversaries by date (YYYY-MM-DD)
const
grouped
=
anniversaries
.
reduce
((
acc
,
it
)
=>
{
const
grouped
=
anniversaries
.
reduce
((
acc
,
it
)
=>
{
const
k
=
isoDate
(
it
.
date
);
const
k
=
isoDate
(
it
.
date
);
...
@@ -33,6 +74,32 @@ export default function StatsTab({ stats, anniversaries = [], openFriend }) {
...
@@ -33,6 +74,32 @@ export default function StatsTab({ stats, anniversaries = [], openFriend }) {
))}
))}
<
/div
>
<
/div
>
{(
textInsights
.
topTerms
.
length
>
0
||
textInsights
.
topLocations
.
length
>
0
)
&&
(
<
div
style
=
{{
marginTop
:
16
}}
>
<
div
className
=
{
styles
.
cardHeader
}
><
span
>
Text
Insights
<
/span></
div
>
{
textInsights
.
topTerms
.
length
>
0
?
(
<
div
style
=
{{
marginBottom
:
8
}}
>
<
div
className
=
{
styles
.
itemMeta
}
style
=
{{
marginBottom
:
4
}}
>
Top
keywords
<
/div
>
<
div
className
=
{
styles
.
tagRow
}
>
{
textInsights
.
topTerms
.
map
(({
term
})
=>
(
<
span
key
=
{
term
}
className
=
{
styles
.
tagChip
}
>
{
term
}
<
/span
>
))}
<
/div
>
<
/div
>
)
:
null
}
{
textInsights
.
topLocations
.
length
>
0
?
(
<
div
>
<
div
className
=
{
styles
.
itemMeta
}
style
=
{{
marginBottom
:
4
}}
>
Top
locations
<
/div
>
<
div
className
=
{
styles
.
tagRow
}
>
{
textInsights
.
topLocations
.
map
(({
name
,
count
})
=>
(
<
span
key
=
{
name
}
className
=
{
styles
.
tagChip
}
>
📍
{
name
}
×
{
count
}
<
/span
>
))}
<
/div
>
<
/div
>
)
:
null
}
<
/div
>
)}
{
annivDays
.
length
>
0
&&
(
{
annivDays
.
length
>
0
&&
(
<
div
style
=
{{
marginTop
:
16
}}
>
<
div
style
=
{{
marginTop
:
16
}}
>
<
div
className
=
{
styles
.
cardHeader
}
>
<
div
className
=
{
styles
.
cardHeader
}
>
...
...
next/components/layout.js
View file @
a3812f69
...
@@ -4,6 +4,7 @@ import styles from "./layout.module.css";
...
@@ -4,6 +4,7 @@ import styles from "./layout.module.css";
import
utilStyles
from
"../styles/utils.module.css"
;
import
utilStyles
from
"../styles/utils.module.css"
;
import
Link
from
"next/link"
;
import
Link
from
"next/link"
;
import
Router
from
'next/router'
import
Router
from
'next/router'
import
{
useRouter
}
from
'next/router'
const
name
=
"PLN"
;
const
name
=
"PLN"
;
export
const
siteTitle
=
"PLN's Works"
;
export
const
siteTitle
=
"PLN's Works"
;
...
@@ -12,6 +13,9 @@ export const twitterHandle = "@PaulLouisNech";
...
@@ -12,6 +13,9 @@ export const twitterHandle = "@PaulLouisNech";
export
const
description
=
"PLN's Selected Works"
;
export
const
description
=
"PLN's Selected Works"
;
export
default
function
Layout
({
children
,
home
})
{
export
default
function
Layout
({
children
,
home
})
{
const
router
=
useRouter
();
const
path
=
router
?.
asPath
||
router
?.
pathname
||
''
;
const
isDunbar
=
path
.
startsWith
(
'/dunbar'
);
// Simple feedback launcher: prompts for text then opens default mail client
// Simple feedback launcher: prompts for text then opens default mail client
const
handleFeedbackMail
=
()
=>
{
const
handleFeedbackMail
=
()
=>
{
try
{
try
{
...
@@ -98,6 +102,8 @@ export default function Layout({ children, home }) {
...
@@ -98,6 +102,8 @@ export default function Layout({ children, home }) {
>
>
▲
▲
<
/a
>
<
/a
>
{
isDunbar
&&
(
<>
{
' '
}
|
{
' '
}
{
' '
}
|
{
' '
}
<
button
<
button
type
=
"button"
type
=
"button"
...
@@ -108,6 +114,8 @@ export default function Layout({ children, home }) {
...
@@ -108,6 +114,8 @@ export default function Layout({ children, home }) {
>
>
Feedback
(
dunbar
@
nech
.
pl
)
Feedback
(
dunbar
@
nech
.
pl
)
<
/button
>
<
/button
>
<
/
>
)}
<
/footer
>
<
/footer
>
<
/div
>
<
/div
>
);
);
...
...
next/lib/dunbar-nlp/index.js
0 → 100644
View file @
a3812f69
This diff is collapsed.
Click to expand it.
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