@@ -35,9 +35,9 @@ export interface TokenWithWeight {
35
35
weight : number ;
36
36
selected ?: boolean ;
37
37
pinned ?: boolean ;
38
- onClick ?: ( e : Event ) => void ;
39
- onMouseover ?: ( e : Event ) => void ;
40
- onMouseout ?: ( e : Event ) => void ;
38
+ onClick ?: ( e : MouseEvent ) => void ;
39
+ onMouseover ?: ( e : MouseEvent ) => void ;
40
+ onMouseout ?: ( e : MouseEvent ) => void ;
41
41
disableHover ?: boolean ;
42
42
forceShowTooltip ?: boolean ;
43
43
}
@@ -50,9 +50,33 @@ export class TokenChips extends LitElement {
50
50
// List of tokens to display
51
51
@property ( { type : Array } ) tokensWithWeights : TokenWithWeight [ ] = [ ] ;
52
52
@property ( { type : Object } ) cmap : SalienceCmap = new UnsignedSalienceCmap ( ) ;
53
- @property ( { type : String } )
54
- tokenGroupTitle ?: string ; // can be used for gradKey
53
+ // Group title, such as the name of the active salience method.
54
+ @property ( { type : String } ) tokenGroupTitle ?: string ;
55
+ /**
56
+ * Dense mode, for less padding and smaller margins around each chip.
57
+ */
55
58
@property ( { type : Boolean } ) dense = false ;
59
+ /**
60
+ * Block mode uses display: block and inline elements for chips, instead of
61
+ * a flex-row layout. This allows chips to flow across line breaks, behaving
62
+ * more like <span> elements and giving a much better experience for larger
63
+ * segments like sentences. However, this comes at the cost of more spacing
64
+ * artifacts and occasional issues with tooltip positioning.
65
+ */
66
+ @property ( { type : Boolean } ) displayBlock = false ;
67
+ /**
68
+ * breakNewlines removes \n at the beginning or end of a segment and inserts
69
+ * explicit row break elements instead. Improves readability in many settings,
70
+ * at the cost of "faithfulness" to the original token text.
71
+ */
72
+ @property ( { type : Boolean } ) breakNewlines = false ;
73
+ /**
74
+ * preSpace removes a leading space from a token and inserts an explicit
75
+ * spacer element instead. Improves readability in many settings by giving
76
+ * natural space between the highlight area for adjacent words, albeit at the
77
+ * cost of hiding where the actual spaces are in the tokenization.
78
+ */
79
+ @property ( { type : Boolean } ) preSpace = false ;
56
80
57
81
static override get styles ( ) {
58
82
return [ sharedStyles , styles ] ;
@@ -71,17 +95,56 @@ export class TokenChips extends LitElement {
71
95
'color' : this . cmap . textCmap ( tokenInfo . weight ) ,
72
96
} ) ;
73
97
74
- // clang-format off
98
+ let tokenText = tokenInfo . token ;
99
+
100
+ let preSpace = false ;
101
+ if ( this . preSpace && tokenText . startsWith ( ' ' ) ) {
102
+ preSpace = true ;
103
+ tokenText = tokenText . slice ( 1 ) ;
104
+ }
105
+
106
+ // TODO(b/324955623): render a gray '⏎' for newlines?
107
+ // Maybe make this a toggleable option, as it can be distracting.
108
+ // TODO(b/324955623): better rendering for multiple newlines, like \n\n\n ?
109
+ // Consider adding an extra ' ' on each line.
110
+
111
+ let preBreak = false ;
112
+ let postBreak = false ;
113
+ if ( this . breakNewlines ) {
114
+ // Logic:
115
+ // - \n : post-break, so blank space goes on previous line
116
+ // - foo\n : post-break
117
+ // - \nfoo : pre-break
118
+ // - \n\n : pre- and post-break, shows a space on its own line
119
+ // - \n\n\n : pre- and post-break, two lines with only spaces
120
+ if ( tokenText . endsWith ( '\n' ) ) {
121
+ // Prefer post-break because this puts the blank space on the end of the
122
+ // previous line, rather than creating an awkward indent on the next
123
+ // one.
124
+ tokenText = tokenText . slice ( 0 , - 1 ) + ' ' ;
125
+ postBreak = true ;
126
+ }
127
+ if ( tokenText . startsWith ( '\n' ) ) {
128
+ // Pre-break only if \n precedes some other text.
129
+ preBreak = true ;
130
+ tokenText = ' ' + tokenText . slice ( 1 ) ;
131
+ }
132
+ }
133
+
134
+ // prettier-ignore
75
135
return html `
136
+ ${ preBreak ? html `< div class ='row-break '> </ div > ` : null }
137
+ ${ preSpace ? html `< div class ='word-spacer '> </ div > ` : null }
76
138
< div class =${ tokenClass } style =${ tokenStyle } @click=${ tokenInfo . onClick }
77
139
@mouseover=${ tokenInfo . onMouseover } @mouseout=${ tokenInfo . onMouseout } >
78
140
< lit-tooltip content =${ tokenInfo . weight . toPrecision ( 3 ) }
79
141
?forceShow =${ Boolean ( tokenInfo . forceShowTooltip ) }
80
142
?disabled=${ Boolean ( tokenInfo . disableHover ) } >
81
- < span class ='pre-wrap ' slot ="tooltip-anchor "> ${ tokenInfo . token } </ span >
143
+ < span class ='pre-wrap ' slot ="tooltip-anchor "> ${ tokenText } </ span >
82
144
</ lit-tooltip >
83
- </ div > ` ;
84
- // clang-format on
145
+ </ div >
146
+ ${ postBreak ? html `< div class ='row-break '> </ div > ` : null }
147
+ ` ;
85
148
}
86
149
87
150
override render ( ) {
@@ -92,17 +155,17 @@ export class TokenChips extends LitElement {
92
155
const holderClass = classMap ( {
93
156
'tokens-holder' : true ,
94
157
'tokens-holder-dense' : this . dense ,
158
+ 'tokens-holder-display-block' : this . displayBlock ,
95
159
} ) ;
96
160
97
- // clang-format off
161
+ // prettier-ignore
98
162
return html `
99
163
< div class ="tokens-group ">
100
164
${ this . tokenGroupTitle ? this . tokenGroupTitle : '' }
101
165
< div class =${ holderClass } >
102
166
${ tokensDOM }
103
167
</ div >
104
168
</ div > ` ;
105
- // clang-format on
106
169
}
107
170
}
108
171
0 commit comments